Index: Makefile.classic
==================================================================
--- Makefile.classic
+++ Makefile.classic
@@ -36,11 +36,11 @@
 #TCC = gcc -O6
 #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
 TCC = gcc -g -Os -Wall
 
 # To add support for HTTPS
-TCC += -DFOSSIL_ENABLE_SSL
+#TCC += -DFOSSIL_ENABLE_SSL
 
 #### Extra arguments for linking the finished binary.  Fossil needs
 #    to link against the Z-Lib compression library.  There are no
 #    other dependencies.  We sometimes add the -static option here
 #    so that we can build a static executable that will run in a
@@ -47,11 +47,11 @@
 #    chroot jail.
 #
 LIB = -lz $(LDFLAGS)
 
 # If using HTTPS:
-LIB += -lcrypto -lssl
+#LIB += -lcrypto -lssl
 
 #### Tcl shell for use in running the fossil testsuite.  If you do not
 #    care about testing the end result, this can be blank.
 #
 TCLSH = tclsh

Index: src/attach.c
==================================================================
--- src/attach.c
+++ src/attach.c
@@ -49,19 +49,37 @@
      "  FROM attachment",
      -1
   );
   if( zPage ){
     if( g.perm.RdWiki==0 ) login_needed();
-    style_header("Attachments To %h", zPage);
+    style_header(
+#ifdef LANG_RU
+      "Файлы к %h",
+#elif LANG_EN
+      "Attachments To %h",
+#endif
+      zPage);
     blob_appendf(&sql, " WHERE target=%Q", zPage);
   }else if( zTkt ){
     if( g.perm.RdTkt==0 ) login_needed();
-    style_header("Attachments To Ticket %.10s", zTkt);
+    style_header(
+#ifdef LANG_RU
+      "Файлы к задаче %.10s",
+#elif LANG_EN
+      "Attachments To Ticket %.10s",
+#endif
+      zTkt);
     blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt);
   }else{
     if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
-    style_header("All Attachments");
+    style_header(
+#ifdef LANG_RU
+      "Все файлы"
+#elif LANG_EN
+      "All Attachments"
+#endif
+      );
   }
   blob_appendf(&sql, " ORDER BY mtime DESC");
   db_prepare(&q, "%s", blob_str(&sql));
   @ <ol>
   while( db_step(&q)==SQLITE_ROW ){
@@ -91,36 +109,72 @@
     @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a>
     if( moderation_pending(attachid) ){
       @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
     }
     @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
-    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
+    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">
+#ifdef LANG_RU
+    @ скачать
+#elif LANG_EN
+    @ download
+#endif
+    @ </a>]<br />
     if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
     if( zComment && zComment[0] ){
       @ %w(zComment)<br />
     }
     if( zPage==0 && zTkt==0 ){
       if( zSrc==0 || zSrc[0]==0 ){
+#ifdef LANG_RU
+        zSrc = "Удалён из";
+#elif LANG_EN
         zSrc = "Deleted from";
+#endif
       }else {
+#ifdef LANG_RU
+        zSrc = "Добавлен к";
+#elif LANG_EN
         zSrc = "Added to";
+#endif
       }
       if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
-        @ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
+#ifdef LANG_RU
+          @ %s(zSrc) задач%s(zSrc==0 || zSrc[0]==0 ? "и" : "е")
+#elif LANG_EN
+        @ %s(zSrc) ticket
+#endif
+        @ <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
         @ %S(zTarget)</a>
       }else{
-        @ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
+#ifdef LANG_RU
+        @ %s(zSrc) страниц%s(zSrc==0 || zSrc[0]==0 ? "ы" : "е")
+#elif LANG_EN
+        @ %s(zSrc) wiki page
+#endif
+        @ <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
         @ %h(zTarget)</a>
       }
     }else{
       if( zSrc==0 || zSrc[0]==0 ){
+#ifdef LANG_RU
+        @ Удалён
+#elif LANG_EN
         @ Deleted
+#endif
       }else {
+#ifdef LANG_RU
+        @ Добавлен
+#elif LANG_EN
         @ Added
+#endif
       }
     }
+#ifdef LANG_RU
+    @ пользователем %h(zDispUser)
+#elif LANG_EN
     @ by %h(zDispUser) on
+#endif
     hyperlink_to_date(zDate, ".");
     free(zUrlTail);
   }
   db_finalize(&q);
   @ </ol>
@@ -173,17 +227,27 @@
        " ORDER BY mtime DESC LIMIT 1",
        zTarget, zFile
     );
   }
   if( zUUID==0 || zUUID[0]==0 ){
+#ifdef LANG_RU
+    style_header("Нет файлов");
+    @ Нет файлов....
+#elif LANG_EN
     style_header("No Such Attachment");
     @ No such attachment....
+#endif
     style_footer();
     return;
   }else if( zUUID[0]=='x' ){
+#ifdef LANG_RU
+    style_header("Отсутствует");
+    @ Файл был удален
+#elif LANG_EN
     style_header("Missing");
     @ Attachment has been deleted
+#endif
     style_footer();
     return;
   }
   g.perm.Read = 1;
   cgi_replace_parameter("name",zUUID);
@@ -237,11 +301,12 @@
   const char *zName = PD("f:filename","unknown");
   const char *zTarget;
   const char *zTargetType;
   int szContent = atoi(PD("f:bytes","0"));
   int goodCaptcha = 1;
-
+  const char *zComment = PD("comment", "");
+ 
   if( P("cancel") ) cgi_redirect(zFrom);
   if( zPage && zTkt ) fossil_redirect_home();
   if( zPage==0 && zTkt==0 ) fossil_redirect_home();
   login_check_credentials();
   if( zPage ){
@@ -248,21 +313,33 @@
     if( g.perm.ApndWiki==0 || g.perm.Attach==0 ) login_needed();
     if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
       fossil_redirect_home();
     }
     zTarget = zPage;
-    zTargetType = mprintf("Wiki Page <a href=\"%s/wiki?name=%h\">%h</a>",
-                           g.zTop, zPage, zPage);
+    zTargetType = mprintf(
+#ifdef LANG_RU
+      "странице"
+#elif LANG_EN
+      "Wiki Page"
+#endif
+      " <a href=\"%s/wiki?name=%h\">%h</a>",
+        g.zTop, zPage, zPage);
   }else{
     if( g.perm.ApndTkt==0 || g.perm.Attach==0 ) login_needed();
     if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
       zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag" 
                         " WHERE tagname GLOB 'tkt-%q*'", zTkt);
       if( zTkt==0 ) fossil_redirect_home();
     }
     zTarget = zTkt;
-    zTargetType = mprintf("Ticket <a href=\"%s/tktview/%S\">%S</a>",
+    zTargetType = mprintf(
+#ifdef LANG_RU
+      "задаче"
+#elif LANG_EN
+      "Ticket"
+#endif
+      " <a href=\"%s/tktview/%S\">%S</a>",
                           g.zTop, zTkt, zTkt);
   }
   if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
   if( P("cancel") ){
     cgi_redirect(zFrom);
@@ -270,11 +347,10 @@
   if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
     Blob content;
     Blob manifest;
     Blob cksum;
     char *zUUID;
-    const char *zComment;
     char *zDate;
     int rid;
     int i, n;
     int addCompress = 0;
     Manifest *pManifest;
@@ -300,11 +376,10 @@
     }
     zName += n;
     if( zName[0]==0 ) zName = "unknown";
     blob_appendf(&manifest, "A %F%s %F %s\n",
                  zName, addCompress ? ".gz" : "", zTarget, zUUID);
-    zComment = PD("comment", "");
     while( fossil_isspace(zComment[0]) ) zComment++;
     n = strlen(zComment);
     while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; }
     if( n>0 ){
       blob_appendf(&manifest, "C %F\n", zComment);
@@ -317,29 +392,55 @@
     attach_put(&manifest, rid, needModerator);
     assert( blob_is_reset(&manifest) );
     db_end_transaction(0);
     cgi_redirect(zFrom);
   }
+#ifdef LANG_RU
+  style_header("Добавить файл");
+#elif LANG_EN
   style_header("Add Attachment");
+#endif
   if( !goodCaptcha ){
+#ifdef LANG_RU
+    @ <p class="generalError">Ошибка: неверный код.</p>
+#elif LANG_EN
     @ <p class="generalError">Error: Incorrect security code.</p>
+#endif
   }
+#ifdef LANG_RU
+  @ <h2>Добавление файла к %s(zTargetType)</h2>
+#elif LANG_EN
   @ <h2>Add Attachment To %s(zTargetType)</h2>
+#endif
   form_begin("enctype='multipart/form-data'", "%R/attachadd");
   @ <div>
+
+#ifdef LANG_RU
+  @ Файл для добавления:
+#elif LANG_EN
   @ File to Attach:
-  @ <input type="file" name="f" size="60" /><br />
+#endif
+  @ <input type="file" name="f" size="120" /><br />
+#ifdef LANG_RU
+  @ Описание:<br />
+#elif LANG_EN
   @ Description:<br />
-  @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
+#endif
+  @ <textarea name="comment" cols="80" rows="5" wrap="virtual">%h(zComment)</textarea><br />
   if( zTkt ){
     @ <input type="hidden" name="tkt" value="%h(zTkt)" />
   }else{
     @ <input type="hidden" name="page" value="%h(zPage)" />
   }
   @ <input type="hidden" name="from" value="%h(zFrom)" />
+#ifdef LANG_RU
+  @ <input type="submit" name="ok" value="Добавить файл" />
+  @ <input type="submit" name="cancel" value="Отмена" />
+#elif LANG_EN
   @ <input type="submit" name="ok" value="Add Attachment" />
   @ <input type="submit" name="cancel" value="Cancel" />
+#endif
   @ </div>
   captcha_generate();
   @ </form>
   style_footer();
 }
@@ -433,19 +534,28 @@
     md5sum_blob(&manifest, &cksum);
     blob_appendf(&manifest, "Z %b\n", &cksum);
     rid = content_put(&manifest);
     manifest_crosslink(rid, &manifest);
     db_end_transaction(0);
+#ifdef LANG_RU
+    @ <p>Указанный файл будет удален.</p>
+#elif LANG_EN
     @ <p>The attachment below has been deleted.</p>
+#endif
   }
 
   if( P("del")
    && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
   ){
     form_begin(0, "%R/ainfo/%s", zUuid);
+#ifdef LANG_RU
+    @ <p>Подтвердите удаление указанного файла.
+    @ <input type="submit" name="confirm" value="Подтверждаю">
+#elif LANG_EN
     @ <p>Confirm you want to delete the attachment shown below.
     @ <input type="submit" name="confirm" value="Confirm">
+#endif
     @ </form>
   }
 
   isModerator = (zTktUuid && g.perm.ModTkt) || (zWikiName && g.perm.ModWiki);
   if( isModerator && (zModAction = P("modaction"))!=0 ){
@@ -460,64 +570,121 @@
     }
     if( strcmp(zModAction,"approve")==0 ){
       moderation_approve(rid);
     }
   }
+#ifdef LANG_RU
+  style_header("Информация о вложении");
+  style_submenu_element("Как есть", "Как есть", "%R/artifact/%S", zUuid);
+
+  @ <div class="section">Обзор</div>
+  @ <p><table class="label-value">
+  @ <tr><th>ID&nbsp;артефакта:</th>
+#elif LANG_EN
   style_header("Attachment Details");
   style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
 
   @ <div class="section">Overview</div>
   @ <p><table class="label-value">
   @ <tr><th>Artifact&nbsp;ID:</th>
+#endif
   @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
   if( g.perm.Setup ){
     @ (%d(rid))
   }
   modPending = moderation_pending(rid);
   if( modPending ){
+#ifdef LANG_RU
+    @ <span class="modpending">*** Ожидает утверждения модератором ***</span>
+#elif LANG_EN
     @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
+#endif
   }
   if( zTktUuid ){
+#ifdef LANG_RU
+    @ <tr><th>Задача:</th>
+#elif LANG_EN
     @ <tr><th>Ticket:</th>
+#endif
     @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
   }
   if( zWikiName ){
+#ifdef LANG_RU
+    @ <tr><th>Вики-страница:</th>
+#elif LANG_EN
     @ <tr><th>Wiki&nbsp;Page:</th>
+#endif
     @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
   }
+#ifdef LANG_RU
+  @ <tr><th>Дата:</th><td>
+#elif LANG_EN
   @ <tr><th>Date:</th><td>
+#endif
   hyperlink_to_date(zDate, "</td></tr>");
   free(zDate);
+#ifdef LANG_RU
+  @ <tr><th>Пользователь:</th><td>
+#elif LANG_EN
   @ <tr><th>User:</th><td>
+#endif
   hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>");
+#ifdef LANG_RU
+  @ <tr><th>Артефакт&nbsp;:</th>
+#elif LANG_EN
   @ <tr><th>Artifact&nbsp;Attached:</th>
+#endif
   @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a>
   if( g.perm.Setup ){
     @ (%d(ridSrc))
   }
+#ifdef LANG_RU
+  @ <tr><th>Имя файла:</th><td>%h(zName)</td></tr>
+#elif LANG_EN
   @ <tr><th>Filename:</th><td>%h(zName)</td></tr>
+#endif
   zMime = mimetype_from_name(zName);
   if( g.perm.Setup ){
     @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr>
   }
+#ifdef LANG_RU
+  @ <tr><th valign="top">Описание:</th><td valign="top">%h(zDesc)</td></tr>
+#elif LANG_EN
   @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr>
+#endif
   @ </table>
   
   if( isModerator && modPending ){
+#ifdef LANG_RU
+    @ <div class="section">Модерация</div>
+#elif LANG_EN
     @ <div class="section">Moderation</div>
+#endif
     @ <blockquote>
     form_begin(0, "%R/ainfo/%s", zUuid);
     @ <label><input type="radio" name="modaction" value="delete">
+#ifdef LANG_RU
+    @ Удалить это изменение</label><br />
+#elif LANG_EN
     @ Delete this change</label><br />
+#endif
     @ <label><input type="radio" name="modaction" value="approve">
+#ifdef LANG_RU
+    @ Утвердить это изменение</label><br />
+#elif LANG_EN
     @ Approve this change</label><br />
+#endif
     @ <input type="submit" value="Submit">
     @ </form>
     @ </blockquote>
   }
 
+#ifdef LANG_RU
+  @ <div class="section">Содержимое</div>
+#elif LANG_EN
   @ <div class="section">Content Appended</div>
+#endif
   @ <blockquote>
   blob_zero(&attach);
   if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
     const char *z;
     const char *zLn = P("ln");
@@ -531,11 +698,11 @@
       @ %h(z)
       @ </pre>
     }
   }else if( strncmp(zMime, "image/", 6)==0 ){
     @ <img src="%R/raw/%S(zSrc)?m=%s(zMime)"></img>
-    style_submenu_element("Image", "Image", "%R/raw/%S?m=%s", zSrc, zMime);
+    style_submenu_element("Изображение", "Изображение", "%R/raw/%S?m=%s", zSrc, zMime);
   }else{
     int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
     @ <i>(file is %d(sz) bytes of binary data)</i>
   }
   @ </blockquote>
@@ -572,16 +739,24 @@
       @ %s(zHeader)
     }
     cnt++;
     @ <li>
     @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
+#ifdef LANG_RU
+    @ добавлено %h(zDispUser) 
+#elif LANG_EN
     @ added by %h(zDispUser) on
+#endif
     hyperlink_to_date(zDate, ".");
+#ifdef LANG_RU
+    @ [%z(href("%R/ainfo/%s",zUuid))детали</a>]
+#elif LANG_EN
     @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
+#endif
     @ </li>
   }
   if( cnt ){
     @ </ul>
   }
   db_finalize(&q);
   
 }

Index: src/cgi.c
==================================================================
--- src/cgi.c
+++ src/cgi.c
@@ -1242,10 +1242,12 @@
     }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
       cgi_setenv("HTTP_REFERER", zVal);
 #endif
     }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
       cgi_setenv("HTTP_USER_AGENT", zVal);
+    }else if( fossil_strcmp(zFieldName,"x-real-ip:")==0 ){
+      cgi_replace_parameter("REMOTE_ADDR", mprintf("%s",zVal));
     }
   }
   cgi_init();
   cgi_trace(0);
 }

Index: src/checkin.c
==================================================================
--- src/checkin.c
+++ src/checkin.c
@@ -652,11 +652,11 @@
 ){
 #ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
   int b;
   b = db_exists(
     "SELECT 1 FROM event"
-    " WHERE datetime(mtime)>=%Q"
+    " WHERE strftime('%%Y-%%m-%%dT%%H:%%M:%%f', mtime)>=strftime('%%Y-%%m-%%dT%%H:%%M:%%f', %Q)"
     "   AND type='ci' AND objid=%d",
     zDate, rid
   );
   if( b ){
     fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"

Index: src/diff.c
==================================================================
--- src/diff.c
+++ src/diff.c
@@ -45,10 +45,11 @@
 
 /*
 ** These error messages are shared in multiple locations.  They are defined
 ** here for consistency.
 */
+
 #define DIFF_CANNOT_COMPUTE_BINARY \
     "cannot compute difference between binary files\n"
 
 #define DIFF_CANNOT_COMPUTE_SYMLINK \
     "cannot compute difference between symlink and regular file\n"
@@ -709,11 +710,11 @@
         }
       }
     }
     if( c=='\t' ){
       z[j++] = ' ';
-      while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; }
+      while( (k&3)!=3 && k<w ){ z[j++] = ' '; k++; }
     }else if( c=='\r' || c=='\f' ){
       z[j++] = ' ';
     }else if( c=='<' && p->escHtml ){
       memcpy(&z[j], "&lt;", 4);
       j += 4;

Index: src/info.c
==================================================================
--- src/info.c
+++ src/info.c
@@ -85,11 +85,23 @@
     db_prepare(&q, "SELECT uuid, pid, isprim FROM plink JOIN blob ON pid=rid "
                    " WHERE cid=%d"
                    " ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
     while( db_step(&q)==SQLITE_ROW ){
       const char *zUuid = db_column_text(&q, 0);
-      const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:";
+      const char *zType = db_column_int(&q, 2) ?
+#ifdef LANG_RU
+        "родитель:"
+#elif LANG_EN
+        "parent:"
+#endif
+        :
+#ifdef LANG_RU
+      "слито с:"
+#elif LANG_EN
+        "merged-from:"
+#endif
+          ;
       zDate = db_text("",
         "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
         db_column_int(&q, 1)
       );
       fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
@@ -99,11 +111,23 @@
     db_prepare(&q, "SELECT uuid, cid, isprim FROM plink JOIN blob ON cid=rid "
                    " WHERE pid=%d"
                    " ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
     while( db_step(&q)==SQLITE_ROW ){
       const char *zUuid = db_column_text(&q, 0);
-      const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:";
+      const char *zType = db_column_int(&q, 2) ?
+#ifdef LANG_RU
+        "потомок:"
+#elif LANG_EN
+        "child:"
+#endif
+        :
+#ifdef LANG_RU
+      "слито в:"
+#elif LANG_EN
+        "merged-into:"
+#endif
+        ;
       zDate = db_text("",
         "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
         db_column_int(&q, 1)
       );
       fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
@@ -111,15 +135,27 @@
     }
     db_finalize(&q);
   }
   zTags = info_tags_of_checkin(rid, 0);
   if( zTags && zTags[0] ){
-    fossil_print("tags:         %s\n", zTags);
+    fossil_print(
+#ifdef LANG_RU
+      "теги:         %s\n",
+#elif LANG_EN
+      "tags:         %s\n",
+#endif
+      zTags);
   }
   free(zTags);
   if( zComment ){
-    fossil_print("comment:      ");
+    fossil_print(
+#ifdef LANG_RU
+      "комментарий:      "
+#elif LANG_EN
+      "comment:      "
+#endif
+      );
     comment_print(zComment, 14, 79);
     free(zComment);
   }
 }
 
@@ -461,12 +497,22 @@
   login_check_credentials();
   if( !g.perm.Read ){ login_needed(); return; }
   zName = P("name");
   rid = name_to_rid_www("name");
   if( rid==0 ){
-    style_header("Check-in Information Error");
+    style_header(
+#ifdef LANG_RU
+      "Ошибка информации о фиксации"
+#elif LANG_EN
+      "Check-in Information Error"
+#endif
+      );
+#ifdef LANG_RU
+    @ Объект не найден: %h(g.argv[2])
+#elif LANG_EN
     @ No such object: %h(g.argv[2])
+#endif
     style_footer();
     return;
   }
   zRe = P("regex");
   if( zRe ) re_compile(&pRe, zRe, 0);
@@ -486,11 +532,17 @@
      rid, rid
   );
   sideBySide = atoi(PD("sbs","1"));
   if( db_step(&q)==SQLITE_ROW ){
     const char *zUuid = db_column_text(&q, 0);
-    char *zTitle = mprintf("Check-in [%.10s]", zUuid);
+    char *zTitle = mprintf(
+#ifdef LANG_RU
+      "Фиксация [%.10s]",
+#elif LANG_EN
+      "Check-in [%.10s]",
+#endif
+      zUuid);
     char *zEUser, *zEComment;
     const char *zUser;
     const char *zComment;
     const char *zDate;
     const char *zOrigDate;
@@ -506,37 +558,80 @@
                    TAG_COMMENT, rid);
     zUser = db_column_text(&q, 2);
     zComment = db_column_text(&q, 3);
     zDate = db_column_text(&q,1);
     zOrigDate = db_column_text(&q, 4);
+#ifdef LANG_RU
+    @ <div class="section">Обзор</div>
+#elif LANG_EN
     @ <div class="section">Overview</div>
+#endif
     @ <table class="label-value">
-    @ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
+    @ <tr><th>
+#ifdef LANG_RU
+    @ SHA1&nbsp;хэш:
+#elif LANG_EN
+    @ SHA1&nbsp;Hash:
+#endif
+    @ </th><td>%s(zUuid)
     if( g.perm.Setup ){
+#ifdef LANG_RU
+      @ (ID записи: %d(rid))
+#elif LANG_EN
       @ (Record ID: %d(rid))
+#endif
     }
     @ </td></tr>
-    @ <tr><th>Date:</th><td>
+#ifdef LANG_RU
+      @ <tr><th>Дата:</th><td>
+#elif LANG_EN
+      @ <tr><th>Date:</th><td>
+#endif
     hyperlink_to_date(zDate, "</td></tr>");
     if( zOrigDate && fossil_strcmp(zDate, zOrigDate)!=0 ){
+#ifdef LANG_RU
+      @ <tr><th>Оригинальная&nbsp;дата:</th><td>
+#elif LANG_EN
       @ <tr><th>Original&nbsp;Date:</th><td>
+#endif
       hyperlink_to_date(zOrigDate, "</td></tr>");
     }
     if( zEUser ){
+#ifdef LANG_RU
+      @ <tr><th>Исправлявший&nbsp;пользователь:</th><td>
+#elif LANG_EN
       @ <tr><th>Edited&nbsp;User:</th><td>
+#endif
       hyperlink_to_user(zEUser,zDate,"</td></tr>");
+#ifdef LANG_RU
+      @ <tr><th>Первый&nbsp;пользователь:</th><td>
+#elif LANG_EN
       @ <tr><th>Original&nbsp;User:</th><td>
+#endif
       hyperlink_to_user(zUser,zDate,"</td></tr>");
     }else{
+#ifdef LANG_RU
+      @ <tr><th>Пользователь:</th><td>
+#elif LANG_EN
       @ <tr><th>User:</th><td>
+#endif
       hyperlink_to_user(zUser,zDate,"</td></tr>");
     }
     if( zEComment ){
+#ifdef LANG_RU
+      @ <tr><th>Исправленный&nbsp;комментарий:</th><td>%w(zEComment)</td></tr>
+      @ <tr><th>Первоначальный&nbsp;комментарий:</th><td>%w(zComment)</td></tr>
+#elif LANG_EN
       @ <tr><th>Edited&nbsp;Comment:</th><td>%w(zEComment)</td></tr>
       @ <tr><th>Original&nbsp;Comment:</th><td>%w(zComment)</td></tr>
+#endif
     }else{
+#ifdef LANG_RU
+      @ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
+#elif LANG_EN
       @ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
+#endif
     }
     if( g.perm.Admin ){
       db_prepare(&q,
          "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
          "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
@@ -546,17 +641,35 @@
       if( db_step(&q)==SQLITE_ROW ){
         const char *zIpAddr = db_column_text(&q, 0);
         const char *zUser = db_column_text(&q, 1);
         const char *zDate = db_column_text(&q, 2);
         if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
+#ifdef LANG_RU
+        @ <tr><th>Получено&nbsp;от:</th>
+        @ <td>%h(zUser) @ %h(zIpAddr) %s(zDate)</td></tr>
+#elif LANG_EN
         @ <tr><th>Received&nbsp;From:</th>
         @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
+#endif
       }
       db_finalize(&q);
     }
     if( g.perm.Hyperlink ){
       const char *zProjName = db_get("project-name", "unnamed");
+#ifdef LANG_RU
+      @ <tr><th>События:</th><td>
+      @   %z(href("%R/timeline?f=%S",zUuid))ближние</a>
+      if( zParent ){
+        @ | %z(href("%R/timeline?p=%S",zUuid))родители</a>
+      }
+      if( !isLeaf ){
+        @ | %z(href("%R/timeline?d=%S",zUuid))потомки</a>
+      }
+      if( zParent && !isLeaf ){
+        @ | %z(href("%R/timeline?dp=%S",zUuid))обои</a>
+      }
+#elif LANG_EN
       @ <tr><th>Timelines:</th><td>
       @   %z(href("%R/timeline?f=%S",zUuid))family</a>
       if( zParent ){
         @ | %z(href("%R/timeline?p=%S",zUuid))ancestors</a>
       }
@@ -564,10 +677,11 @@
         @ | %z(href("%R/timeline?d=%S",zUuid))descendants</a>
       }
       if( zParent && !isLeaf ){
         @ | %z(href("%R/timeline?dp=%S",zUuid))both</a>
       }
+#endif
       db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
                      " WHERE rid=%d AND tagtype>0 "
                      "   AND tag.tagid=tagxref.tagid "
                      "   AND +tag.tagname GLOB 'sym-*'", rid);
       while( db_step(&q)==SQLITE_ROW ){
@@ -578,78 +692,151 @@
 
 
       /* The Download: line */
       if( g.perm.Zip ){
         char *zUrl = mprintf("%R/tarball/%t-%S.tar.gz?uuid=%s",
-                             zProjName, zUuid, zUuid);
+          zProjName, zUuid, zUuid);
         @ </td></tr>
         @ <tr><th>Downloads:</th><td>
         @ %z(href("%s",zUrl))Tarball</a>
         @ | %z(href("%R/zip/%t-%S.zip?uuid=%s",zProjName,zUuid,zUuid))
+#ifdef LANG_RU
+        @         ZIP архив</a>
+#elif LANG_EN
         @         ZIP archive</a>
+#endif
         fossil_free(zUrl);
       }
-      @ </td></tr>
+	  @ </td></tr>
+#ifdef LANG_RU
+		  @ <tr><th>Другие&nbsp;ссылки:</th>
+		  @   <td>
+		  @     %z(href("%R/dir?ci=%S",zUuid))файлы</a>
+		  @   | %z(href("%R/artifact/%S",zUuid))манифест</a>
+#elif LANG_EN
       @ <tr><th>Other&nbsp;Links:</th>
       @   <td>
       @     %z(href("%R/dir?ci=%S",zUuid))files</a>
       @   | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
       @   | %z(href("%R/artifact/%S",zUuid))manifest</a>
+#endif
       if( g.perm.Write ){
+#ifdef LANG_RU
+        @   | %z(href("%R/ci_edit?r=%S",zUuid))изменить</a>
+#elif LANG_EN
         @   | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
+#endif
       }
       @   </td>
       @ </tr>
     }
     @ </table>
   }else{
-    style_header("Check-in Information");
+    style_header(
+#ifdef LANG_RU
+      "Информация о фиксации"
+#elif LANG_EN
+      "Check-in Information"
+#endif
+      );
     login_anonymous_available();
   }
   db_finalize(&q);
   showTags(rid, "");
   if( zParent ){
+#ifdef LANG_RU
+    @ <div class="section">Изменения</div>
+#elif LANG_EN
     @ <div class="section">Changes</div>
+#endif
     @ <div class="sectionmenu">
     showDiff = g.zPath[0]!='c';
     if( db_get_boolean("show-version-diffs", 0)==0 ){
       showDiff = !showDiff;
       if( showDiff ){
         @ %z(xhref("class='button'","%R/vinfo/%T",zName))
+#ifdef LANG_RU
+        @ спрятать&nbsp;различия</a>
+#elif LANG_EN
         @ hide&nbsp;diffs</a>
+#endif
         if( sideBySide ){
           @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
+#ifdef LANG_RU
+          @ различия&nbsp;разом</a>
+#elif LANG_EN
           @ unified&nbsp;diffs</a>
+#endif
         }else{
           @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
+#ifdef LANG_RU
+          @ различия&nbsp;бок-о-бок</a>
+#elif LANG_EN
           @ side-by-side&nbsp;diffs</a>
+#endif
         }
       }else{
         @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
+#ifdef LANG_RU
+        @ показать&nbsp;различия&nbsp;разом</a>
+#elif LANG_EN
         @ show&nbsp;unified&nbsp;diffs</a>
+#endif
         @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
+#ifdef LANG_RU
+        @ показать&nbsp;различия&nbsp;бок-о-бок</a>
+#elif LANG_EN
         @ show&nbsp;side-by-side&nbsp;diffs</a>
+#endif
       }
     }else{
       if( showDiff ){
-        @ %z(xhref("class='button'","%R/ci/%T",zName))hide&nbsp;diffs</a>
+        @ %z(xhref("class='button'","%R/ci/%T",zName))
+#ifdef LANG_RU
+        @ спрятать&nbsp;различия</a>
+#elif LANG_EN
+        @ hide&nbsp;diffs</a>
+#endif
         if( sideBySide ){
           @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName))
+#ifdef LANG_RU
+          @ различия&nbsp;разом</a>
+#elif LANG_EN
           @ unified&nbsp;diffs</a>
+#endif
         }else{
           @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName))
+#ifdef LANG_RU
+          @ различия&nbsp;бок-о-бок</a>
+#elif LANG_EN
           @ side-by-side&nbsp;diffs</a>
+#endif
         }
       }else{
         @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName))
+#ifdef LANG_RU
+          @ показать&nbsp;различия&nbsp;разом</a>
+#elif LANG_EN
         @ show&nbsp;unified&nbsp;diffs</a>
+#endif
         @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName))
+#ifdef LANG_RU
+        @ показать&nbsp;различия&nbsp;бок-о-бок</a>
+#elif LANG_EN
         @ show&nbsp;side-by-side&nbsp;diffs</a>
+#endif
       }
     }
+    @ &nbsp;&nbsp;
     @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
-    @ patch</a></div>
+#ifdef LANG_RU
+    @ патч</a>
+#elif LANG_EN
+    @ patch</a>
+#endif
+    @</div>
+    
     if( pRe ){
       @ <p><b>Only differences that match regular expression "%h(zRe)"
       @ are shown.</b></p>
     }
     db_prepare(&q,
@@ -696,12 +883,22 @@
 
   login_check_credentials();
   if( !g.perm.RdWiki ){ login_needed(); return; }
   rid = name_to_rid_www("name");
   if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI))==0 ){
-    style_header("Wiki Page Information Error");
+    style_header(
+#ifdef LANG_RU
+      "Ошибка информации о странице"
+#elif LANG_EN
+      "Wiki Page Information Error"
+#endif
+      );
+#ifdef LANG_RU
+    @ Объект не найден: %h(g.argv[2])
+#elif LANG_EN
     @ No such object: %h(P("name"))
+#endif
     style_footer();
     return;
   }
   if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
     if( strcmp(zModAction,"delete")==0 ){
@@ -711,62 +908,117 @@
     }
     if( strcmp(zModAction,"approve")==0 ){
       moderation_approve(rid);
     }
   }
+#ifdef LANG_RU
+  style_header("Правка \"%h\"", pWiki->zWikiTitle);
+#elif LANG_EN
   style_header("Update of \"%h\"", pWiki->zWikiTitle);
+#endif
   zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   zDate = db_text(0, "SELECT datetime(%.17g)", pWiki->rDate);
+#ifdef LANG_RU
+  style_submenu_element("Как есть", "Как есть", "artifact/%S", zUuid);
+  style_submenu_element("История", "История", "whistory?name=%t",
+                        pWiki->zWikiTitle);
+  style_submenu_element("Страница", "Страница", "wiki?name=%t",
+                        pWiki->zWikiTitle);
+#elif LANG_EN
   style_submenu_element("Raw", "Raw", "artifact/%S", zUuid);
   style_submenu_element("History", "History", "whistory?name=%t",
                         pWiki->zWikiTitle);
   style_submenu_element("Page", "Page", "wiki?name=%t",
                         pWiki->zWikiTitle);
+#endif
   login_anonymous_available();
+#ifdef LANG_RU
+  @ <div class="section">Обзор</div>
+#elif LANG_EN
   @ <div class="section">Overview</div>
+#endif
   @ <p><table class="label-value">
+#ifdef LANG_RU
+  @ <tr><th>ID&nbsp;артефакта:</th>
+#elif LANG_EN
   @ <tr><th>Artifact&nbsp;ID:</th>
+#endif
   @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
   if( g.perm.Setup ){
     @ (%d(rid))
   }
   modPending = moderation_pending(rid);
   if( modPending ){
+#ifdef LANG_RU
+    @ <span class="modpending">*** Ожидает утверждения модератором ***</span>
+#elif LANG_EN
     @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
+#endif
   }
   @ </td></tr>
+#ifdef LANG_RU
+  @ <tr><th>Имя&nbsp;страницы:</th><td>%h(pWiki->zWikiTitle)</td></tr>
+  @ <tr><th>Дата:</th><td>
+  hyperlink_to_date(zDate, "</td></tr>");
+  @ <tr><th>Создавший&nbsp;пользователь:</th><td>
+#elif LANG_EN
   @ <tr><th>Page&nbsp;Name:</th><td>%h(pWiki->zWikiTitle)</td></tr>
   @ <tr><th>Date:</th><td>
   hyperlink_to_date(zDate, "</td></tr>");
   @ <tr><th>Original&nbsp;User:</th><td>
+#endif
   hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
   if( pWiki->nParent>0 ){
     int i;
+#ifdef LANG_RU
+    @ <tr><th>Родител%s(pWiki->nParent==1?"ь":"и"):</th><td>
+#elif LANG_EN
     @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
+#endif
     for(i=0; i<pWiki->nParent; i++){
       char *zParent = pWiki->azParent[i];
       @ %z(href("info/%S",zParent))%s(zParent)</a>
     }
     @ </td></tr>
   }
   @ </table>
 
   if( g.perm.ModWiki && modPending ){
+#ifdef LANG_RU
+    @ <div class="section">Модерация</div>
+#elif LANG_EN
     @ <div class="section">Moderation</div>
+#endif
     @ <blockquote>
     @ <form method="POST" action="%R/winfo/%s(zUuid)">
     @ <label><input type="radio" name="modaction" value="delete">
+#ifdef LANG_RU
+    @ Удалить это изменение</label><br />
+#elif LANG_EN
     @ Delete this change</label><br />
+#endif
     @ <label><input type="radio" name="modaction" value="approve">
+#ifdef LANG_RU
+    @ Утвердить это изменение</label><br />
+#elif LANG_EN
     @ Approve this change</label><br />
+#endif
+#ifdef LANG_RU
+    @ <input type="submit" value="Ок">
+#elif LANG_EN
     @ <input type="submit" value="Submit">
+#endif
     @ </form>
     @ </blockquote>
   }
 
 
+#ifdef LANG_RU
+  @ <div class="section">Содержимое</div>
+#elif LANG_EN
   @ <div class="section">Content</div>
+#endif
   blob_init(&wiki, pWiki->zWiki, -1);
   wiki_convert(&wiki, 0, 0);
   blob_reset(&wiki);
   manifest_destroy(pWiki);
   style_footer();
@@ -779,12 +1031,17 @@
   va_list ap;
   const char *z;
   va_start(ap, zFormat);
   z = vmprintf(zFormat, ap);
   va_end(ap);
+#ifdef LANG_RU
+  style_header("Ошибка URL");
+  @ <h1>Ошибка</h1>
+#elif LANG_EN
   style_header("URL Error");
   @ <h1>Error</h1>
+#endif
   @ <p>%h(z)</p>
   style_footer();
 }
 
 /*
@@ -794,20 +1051,38 @@
 static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
   int rid;
 
   *pRid = rid = name_to_rid_www(zParam);
   if( rid==0 ){
-    const char *z = P(zParam);
-    if( z==0 || z[0]==0 ){
-      webpage_error("Missing \"%s\" query parameter.", zParam);
-    }else{
-      webpage_error("No such artifact: \"%s\"", z);
-    }
-    return 0;
+	  const char *z = P(zParam);
+	  if( z==0 || z[0]==0 ){
+		  webpage_error(
+#ifdef LANG_RU
+			  "Отсутствует параметр \"%s\" в запросе."
+#elif LANG_EN
+			  "Missing \"%s\" query parameter."
+#endif
+			  , zParam);
+	  }else{
+		  webpage_error(
+#ifdef LANG_RU
+			  "Артефакт не найден: \"%s\""
+#elif LANG_EN
+			  "No such artifact: \"%s\""
+#endif
+			  , z);
+	  }
+      return 0;
   }
   if( !is_a_version(rid) ){
-    webpage_error("Artifact %s is not a checkin.", P(zParam));
+    webpage_error(
+#ifdef LANG_RU
+      "Артефакт не Фиксация.",
+#elif LANG_EN
+      "Artifact %s is not a checkin.",
+#endif
+      P(zParam));
     return 0;
   }
   return manifest_get(rid, CFTYPE_MANIFEST);
 }
 
@@ -840,11 +1115,15 @@
     hyperlink_to_uuid(zUuid);
     blob_zero(&comment);
     db_column_blob(&q, 2, &comment);
     wiki_convert(&comment, 0, wikiFlags);
     blob_reset(&comment);
+#ifdef LANG_RU
+    @ (пользователь:
+#elif LANG_EN
     @ (user:
+#endif
     hyperlink_to_user(zUser,zDate,",");
     if( zTagList && zTagList[0] && g.perm.Hyperlink ){
       int i;
       const char *z = zTagList;
       Blob links;
@@ -856,16 +1135,28 @@
               href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
         );
         if( z[i]==0 ) break;
         z += i+2;
       }
+#ifdef LANG_RU
+      @ метка: %s(blob_str(&links)),
+#elif LANG_EN
       @ tags: %s(blob_str(&links)),
+#endif
       blob_reset(&links);
     }else{
-      @ tags: %h(zTagList),
+#ifdef LANG_RU
+      @ метка: %h(zTagList),
+#elif LANG_EN
+      @ метка: %h(zTagList),
+#endif
     }
+#ifdef LANG_RU
+    @ дата:
+#elif LANG_EN
     @ date:
+#endif
     hyperlink_to_date(zDate, ")");
   }
   db_finalize(&q);
 }
 
@@ -917,25 +1208,47 @@
   showDetail = atoi(PD("detail","0"));
   if( !showDetail && sideBySide ) showDetail = 1;
   zFrom = P("from");
   zTo = P("to");
   if( !sideBySide ){
-    style_submenu_element("Side-by-side Diff", "sbsdiff",
+    style_submenu_element(
+#ifdef LANG_RU
+      "Различия бок-о-бок", "sbsdiff",
+#elif LANG_EN
+      "Side-by-side Diff", "sbsdiff",
+#endif
                           "%R/vdiff?from=%T&to=%T&detail=%d&sbs=1",
                           zFrom, zTo, showDetail);
   }else{
-    style_submenu_element("Unified Diff", "udiff",
+    style_submenu_element(
+#ifdef LANG_RU
+      "Различия разом", "udiff",
+#elif LANG_EN
+      "Unified Diff", "udiff",
+#endif
                           "%R/vdiff?from=%T&to=%T&detail=%d&sbs=0",
                           zFrom, zTo, showDetail);
   }
   style_submenu_element("Invert", "invert",
                         "%R/vdiff?from=%T&to=%T&detail=%d&sbs=%d",
                         zTo, zFrom, showDetail, sideBySide);
+#ifdef LANG_RU
+  style_header("Различия в фиксации");
+  @ <h2>Различия между:</h2><blockquote>
+  checkin_description(ridFrom);
+#elif LANG_EN
   style_header("Check-in Differences");
   @ <h2>Difference From:</h2><blockquote>
   checkin_description(ridFrom);
-  @ </blockquote><h2>To:</h2><blockquote>
+#endif
+  @ </blockquote><h2>
+#ifdef LANG_RU
+    @ и:
+#elif LANG_EN
+    @ To:
+#endif
+  @ </h2><blockquote>
   checkin_description(ridTo);
   @ </blockquote>
   if( pRe ){
     @ <p><b>Only differences that match regular expression "%h(zRe)"
     @ are shown.</b></p>
@@ -1273,32 +1586,71 @@
   content_get(v2, &c2);
   text_diff(&c1, &c2, pOut, pRe, diffFlags);
   blob_reset(&c1);
   blob_reset(&c2);
   if( !isPatch ){
+#ifdef LANG_RU
+    style_header("Различия");
+#elif LANG_EN
     style_header("Diff");
-    style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
+#endif
+    style_submenu_element(
+#ifdef LANG_RU
+      "Патч", "Патч",
+#elif LANG_EN
+      "Patch", "Patch",
+#endif
+      "%s/fdiff?v1=%T&v2=%T&patch",
                           g.zTop, P("v1"), P("v2"));
     if( !sideBySide ){
-      style_submenu_element("Side-by-side Diff", "sbsdiff",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Различия бок-о-бок", "sbsdiff",
+#elif LANG_EN
+        "Side-by-side Diff", "sbsdiff",
+#endif
                             "%s/fdiff?v1=%T&v2=%T&sbs=1",
                             g.zTop, P("v1"), P("v2"));
     }else{
-      style_submenu_element("Unified Diff", "udiff",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Различия разом", "udiff",
+#elif LANG_EN
+        "Unified Diff", "udiff",
+#endif
                             "%s/fdiff?v1=%T&v2=%T&sbs=0",
                             g.zTop, P("v1"), P("v2"));
     }
 
     if( P("smhdr")!=0 ){
+#ifdef LANG_RU
+      @ <h2>Различия между артефактом
+#elif LANG_EN
       @ <h2>Differences From Artifact
-      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To
+#endif
+      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>
+#ifdef LANG_RU
+      @ и
+#elif LANG_EN
+      @ To
+#endif
       @ %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>.</h2>
     }else{
-      @ <h2>Differences From
-      @ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
+#ifdef LANG_RU
+      @ <h2>Различия между артефактом
+#elif LANG_EN
+      @ <h2>Differences From Artifact
+#endif
+      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
       object_description(v1, 0, 0);
-      @ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
+      @ <h2>
+#ifdef LANG_RU
+        @ и артефактом
+#elif LANG_EN
+        @ To Artifact
+#endif
+      @ %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
       object_description(v2, 0, 0);
     }
     if( pRe ){
       @ <b>Only differences that match regular expression "%h(zRe)"
       @ are shown.</b>
@@ -1416,27 +1768,55 @@
   if( !g.perm.Read ){ login_needed(); return; }
   if( rid==0 ) fossil_redirect_home();
   if( g.perm.Admin ){
     const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
     if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
-      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Восстановить","Восстановить",
+#elif LANG_EN
+        "Unshun","Unshun",
+#endif
+        "%s/shun?uuid=%s&sub=1",
             g.zTop, zUuid);
     }else{
-      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Стереть","Стереть",
+#elif LANG_EN
+        "Shun","Shun",
+#endif
+        "%s/shun?shun=%s#addshun",
             g.zTop, zUuid);
     }
   }
-  style_header("Hex Artifact Content");
+  style_header(
+#ifdef LANG_RU
+    "Содержимое бинарного артефакта"
+#elif LANG_EN
+    "Hex Artifact Content"
+#endif
+    );
   zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
   if( g.perm.Setup ){
+#ifdef LANG_RU
+    @ <h2>Артефакт %s(zUuid) (%d(rid)):</h2>
+  }else{
+    @ <h2>Artifact %s(zUuid):</h2>
+#elif LANG_EN
     @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
   }else{
     @ <h2>Artifact %s(zUuid):</h2>
+#endif
   }
   blob_zero(&downloadName);
   object_description(rid, 0, &downloadName);
+#ifdef LANG_RU
+  style_submenu_element("Скачать", "Скачать",
+#elif LANG_EN
   style_submenu_element("Download", "Download",
+#endif
         "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
   @ <hr />
   content_get(rid, &content);
   @ <blockquote><pre>
   hexdump(&content);
@@ -1573,23 +1953,42 @@
     }else{
       style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
             g.zTop, zUuid);
     }
   }
+#ifdef LANG_RU
+  style_header("Содержимое артефакта");
+#elif LANG_EN
   style_header("Artifact Content");
+#endif
   zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
   if( g.perm.Setup ){
+#ifdef LANG_RU
+    @ <h2>Артефакт %s(zUuid) (%d(rid)):</h2>
+  }else{
+    @ <h2>Артефакт %s(zUuid):</h2>
+#elif LANG_EN
     @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
   }else{
     @ <h2>Artifact %s(zUuid):</h2>
+#endif
   }
+  @ <blockquote><p>
   blob_zero(&downloadName);
   objType = object_description(rid, 0, &downloadName);
+#ifdef LANG_RU
+  style_submenu_element("Скачать", "Скачать",
+#elif LANG_EN
   style_submenu_element("Download", "Download",
+#endif
           "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid);
   if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
+#ifdef LANG_RU
+    style_submenu_element("Используется в фиксациях", "Используется в фиксациях",
+#elif LANG_EN
     style_submenu_element("Checkins Using", "Checkins Using",
+#endif
           "%R/timeline?uf=%s&n=200",zUuid);
   }
   asText = P("txt")!=0;
   zMime = mimetype_from_name(blob_str(&downloadName));
   if( zMime ){
@@ -1597,20 +1996,35 @@
       if( asText ){
         style_submenu_element("Html", "Html",
                               "%s/artifact/%s", g.zTop, zUuid);
       }else{
         renderAsHtml = 1;
-        style_submenu_element("Text", "Text",
+        style_submenu_element(
+#ifdef LANG_RU
+          "Текст", "Показать как текст",
+#elif LANG_EN
+          "Text", "Text",
+#endif
                               "%s/artifact/%s?txt=1", g.zTop, zUuid);
       }
     }else if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
       if( asText ){
-        style_submenu_element("Wiki", "Wiki",
+        style_submenu_element(
+#ifdef LANG_RU
+          "Вики", "Показать как вики",
+#elif LANG_EN
+          "Wiki", "Wiki",
+#endif
                               "%s/artifact/%s", g.zTop, zUuid);
       }else{
         renderAsWiki = 1;
-        style_submenu_element("Text", "Text",
+        style_submenu_element(
+#ifdef LANG_RU
+          "Текст", "Показать как текст",
+#elif LANG_EN
+          "Text", "Text",
+#endif
                               "%s/artifact/%s?txt=1", g.zTop, zUuid);
       }
     }
   }
   if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
@@ -1644,11 +2058,15 @@
     }else if( strncmp(zMime, "image/", 6)==0 ){
       @ <img src="%R/raw/%S(zUuid)?m=%s(zMime)" />
       style_submenu_element("Image", "Image",
                             "%R/raw/%S?m=%s", zUuid, zMime);
     }else{
+#ifdef LANG_RU
+      @ <i>(бинарный файл, байт: %d(blob_size(&content)))</i>
+#elif LANG_EN
       @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
+#endif
     }
     @ </blockquote>
   }
   style_footer();
 }

Index: src/login.c
==================================================================
--- src/login.c
+++ src/login.c
@@ -493,22 +493,38 @@
                   " WHERE uid=%d"
                   " AND (constant_time_cmp(pw,%Q)=0"
                   "      OR constant_time_cmp(pw,%Q)=0)", 
                   g.userUid, zSha1Pw, zPasswd) ){
       sleep(1);
+#ifdef LANG_RU
+      zErrMsg = 
+      @ <p><span class="loginError">
+      @ Вы ввели неверный старый пароль.
+      @ Ваш пароль не изменен.
+      @ </span></p>
+#elif LANG_EN
       zErrMsg = 
-         @ <p><span class="loginError">
-         @ You entered an incorrect old password while attempting to change
-         @ your password.  Your password is unchanged.
-         @ </span></p>
+      @ <p><span class="loginError">
+      @ You entered an incorrect old password while attempting to change
+      @ your password.  Your password is unchanged.
+      @ </span></p>
+#endif
       ;
     }else if( fossil_strcmp(zNew1,zNew2)!=0 ){
+#ifdef LANG_RU
+      zErrMsg = 
+       @ <p><span class="loginError">
+       @ Два введенных новых пароля не совпадают.
+       @ Ваш пароль не изменен.
+       @ </span></p>
+#elif LANG_EN
       zErrMsg = 
-         @ <p><span class="loginError">
-         @ The two copies of your new passwords do not match.
-         @ Your password is unchanged.
-         @ </span></p>
+       @ <p><span class="loginError">
+       @ The two copies of your new passwords do not match.
+       @ Your password is unchanged.
+       @ </span></p>
+#endif
       ;
     }else{
       char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
       char *zChngPw;
       char *zErr;
@@ -543,14 +559,21 @@
     /* Attempting to log in as a user other than anonymous.
     */
     uid = login_search_uid(zUsername, zPasswd);
     if( uid<=0 ){
       sleep(1);
+#ifdef LANG_RU
       zErrMsg = 
          @ <p><span class="loginError">
+         @ Вы ввели неверный пароль или имя несущестующего пользователя.
+         @ </span></p>
+#elif LANG_EN
+      zErrMsg = 
+        @ <p><span class="loginError">
          @ You entered an unknown user or an incorrect password.
          @ </span></p>
+#endif
       ;
       record_login_attempt(zUsername, zIpAddr, 0);
     }else{
       /* Non-anonymous login is successful.  Set a cookie of the form:
       **
@@ -561,11 +584,17 @@
       */
       login_set_user_cookie(zUsername, uid, NULL);
       redirect_to_g();
     }
   }
-  style_header("Login/Logout");
+  style_header(
+#ifdef LANG_RU
+    "Вход/Выход"
+#elif LANG_EN
+    "Login/Logout"
+#endif
+    );
   @ %s(zErrMsg)
   if( zGoto && P("anon")==0 ){
     @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
   }
   form_begin(0, "%R/login");
@@ -572,29 +601,45 @@
   if( zGoto ){
     @ <input type="hidden" name="g" value="%h(zGoto)" />
   }
   @ <table class="login_out">
   @ <tr>
-  @   <td class="login_out_label">User ID:</td>
+  @   <td class="login_out_label">
+#ifdef LANG_RU
+  @ Пользователь:
+#elif LANG_EN
+  @ User ID:
+#endif
+  @ </td>
   if( anonFlag ){
     @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
   }else{
     @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
   }
   @ </tr>
   @ <tr>
-  @  <td class="login_out_label">Password:</td>
+  @  <td class="login_out_label">
+#ifdef LANG_RU
+  @ Пароль:
+#elif LANG_EN
+  @ Password:
+#endif
+  @ </td>
   @   <td><input type="password" id="p" name="p" value="" size="30" /></td>
   @ </tr>
   if( g.zLogin==0 ){
     zAnonPw = db_text(0, "SELECT pw FROM user"
                          " WHERE login='anonymous'"
                          "   AND cap!=''");
   }
   @ <tr>
   @   <td></td>
+#ifdef LANG_RU
+  @   <td><input type="submit" name="in" value="Вход"
+#elif LANG_EN
   @   <td><input type="submit" name="in" value="Login"
+#endif
   @        onClick="chngAction(this.form)" /></td>
   @ </tr>
   @ </table>
   @ <script type="text/JavaScript">
   @   gebi('u').focus()
@@ -608,52 +653,101 @@
      @     form.action = "%h(zSSL)/login";
      @  }
   }
   @ }
   @ </script>
-  if( g.zLogin==0 ){
-    @ <p>Enter
-  }else{
-    @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
-    @ <p>To change your login to a different user, enter
-  }
-  @ your user-id and password at the left and press the
-  @ "Login" button.  Your user name will be stored in a browser cookie.
-  @ You must configure your web browser to accept cookies in order for
-  @ the login to take.</p>
+#ifdef LANG_RU
+    if( g.zLogin==0 ){
+      @ <p>Укажите
+    }else{
+      @ <p>Вы вошли как <b>%h(g.zLogin)</b></p>
+      @ <p>Чтобы войти под другим именем, укажите
+    }
+    @ имя пользователя и пароль, и нажмите кнопку "Вход".
+    @ Введенное имя будет сохранено в куках браузера.
+    @ Вы должны разрешить браузеру сохранять куки для возможности входа
+    @ на сайт.</p>
+#elif LANG_EN
+    if( g.zLogin==0 ){
+      @ <p>Enter
+    }else{
+      @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
+      @ <p>To change your login to a different user, enter
+    }
+    @ your user-id and password at the left and press the
+    @ "Login" button.  Your user name will be stored in a browser cookie.
+    @ You must configure your web browser to accept cookies in order for
+    @ the login to take.</p>
+#endif
+  
   if( db_get_boolean("self-register", 0) ){
+#ifdef LANG_RU
+    @ <p>Если у вас нет аккаунта, вы можете
+    @ <a href="%s(g.zTop)/register?g=%T(P("G"))">зарегистрироваться</a>.
+#elif LANG_EN
     @ <p>If you do not have an account, you can 
     @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
+#endif
   }
+  
   if( zAnonPw ){
     unsigned int uSeed = captcha_seed();
     char const *zDecoded = captcha_decode(uSeed);
     int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
     char *zCaptcha = captcha_render(zDecoded);
 
     @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
+#ifdef LANG_RU
+    @ Гости могут указать <b>anonymous</b> как имя пользователя
+    @ и ввести 8-символьный пароль, указанный ниже:</p>
+#elif LANG_EN
     @ Visitors may enter <b>anonymous</b> as the user-ID with
     @ the 8-character hexadecimal password shown below:</p>
+#endif
     @ <div class="captcha"><table class="captcha"><tr><td><pre>
     @ %h(zCaptcha)
     @ </pre></td></tr></table>
     if( bAutoCaptcha ) {
-        @ <input type="button" value="Fill out captcha"
+#ifdef LANG_RU
+      @ <input type="button" value="Заполнить капчу"
+#elif LANG_EN
+      @ <input type="button" value="Fill out captcha"
+#endif
         @  onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
     }
     @ </div>
     free(zCaptcha);
   }
   if( g.zLogin ){
     @ <hr />
+#ifdef LANG_RU
+    @ <p>Для выхода
+    @ нажмите эту кнопку:<br />
+    @ <input type="submit" name="out" value="Выход" /></p>
+#elif LANG_EN
     @ <p>To log off the system (and delete your login cookie)
     @  press the following button:<br />
     @ <input type="submit" name="out" value="Logout" /></p>
+#endif
   }
   @ </form>
   if( g.perm.Password ){
     @ <hr />
+#ifdef LANG_RU
+    @ <p>Для смены своего пароля введите текущий пароль и два раза
+    @ новый, после чего нажмите кнопку "Изменить пароль".</p>
+    @ <form action="login" method="post">
+    @ <table>
+    @ <tr><td class="login_out_label">Старый пароль:</td>
+    @ <td><input type="password" name="p" size="30" /></td></tr>
+    @ <tr><td class="login_out_label">Новый пароль:</td>
+    @ <td><input type="password" name="n1" size="30" /></td></tr>
+    @ <tr><td class="login_out_label">Повторите новый пароль:</td>
+    @ <td><input type="password" name="n2" size="30" /></td></tr>
+    @ <tr><td></td>
+    @ <td><input type="submit" value="Изменить пароль" /></td></tr>
+#elif LANG_EN
     @ <p>To change your password, enter your old password and your
     @ new password twice below then press the "Change Password"
     @ button.</p>
     form_begin(0, "%R/login");
     @ <table>
@@ -663,10 +757,11 @@
     @ <td><input type="password" name="n1" size="30" /></td></tr>
     @ <tr><td class="login_out_label">Repeat New Password:</td>
     @ <td><input type="password" name="n2" size="30" /></td></tr>
     @ <tr><td></td>
     @ <td><input type="submit" value="Change Password" /></td></tr>
+#endif
     @ </table>
     @ </form>
   }
   style_footer();
 }
@@ -1145,13 +1240,19 @@
   if( !g.perm.Hyperlink &&
       db_exists("SELECT 1 FROM user"
                 " WHERE login='anonymous'"
                 "   AND cap LIKE '%%h%%'") ){
     const char *zUrl = PD("REQUEST_URI", "index");
+#ifdef LANG_RU
+    @ <p>Многие <span class="disabled">ссылки отключены.</span><br />
+    @ Используйте <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">анонимный вход</a>
+    @ для включения ссылок.</p>
+#elif LANG_EN
     @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
     @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
     @ to enable hyperlinks.</p>
+#endif
   }
 }
 
 /*
 ** While rendering a form, call this routine to add the Anti-CSRF token

Index: src/main.mk
==================================================================
--- src/main.mk
+++ src/main.mk
@@ -8,11 +8,11 @@
 # to regenerate this file.
 #
 # This file is included by primary Makefile.
 #
 
-XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) -I$(OBJDIR)
+XTCC = $(TCC) $(CFLAGS) -DFOSSIL_ENABLE_JSON -DLANG_RU -I. -I$(SRCDIR) -I$(OBJDIR)
 
 
 SRC = \
   $(SRCDIR)/add.c \
   $(SRCDIR)/allrepo.c \

Index: src/manifest.c
==================================================================
--- src/manifest.c
+++ src/manifest.c
@@ -1552,40 +1552,71 @@
       if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
         zNewStatus = pManifest->aField[i].zValue;
       }
     }
     if( zNewStatus ){
+#ifdef LANG_RU
+      blob_appendf(&comment, "%h задача [%.10s]: <i>%h</i>",
+#elif LANG_EN
       blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
+#endif
          zNewStatus, pManifest->zTicketUuid, zTitle
       );
       if( pManifest->nField>1 ){
+#ifdef LANG_RU
+        blob_appendf(&comment, " плюс еще изменений %d",
+#elif LANG_EN
         blob_appendf(&comment, " plus %d other change%s",
+#endif
           pManifest->nField-1, pManifest->nField==2 ? "" : "s");
       }
+#ifdef LANG_RU
+      blob_appendf(&brief, "%h задача [%.10s].",
+#elif LANG_EN
       blob_appendf(&brief, "%h ticket [%.10s].",
+#endif
                    zNewStatus, pManifest->zTicketUuid);
     }else{
       zNewStatus = db_text("unknown", 
          "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
          zStatusColumn, pManifest->zTicketUuid
       );
+#ifdef LANG_RU
+      blob_appendf(&comment, "Задача [%.10s] <i>%h</i> статус остался %h с "
+           "%d изменением",
+		   pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField
+#elif LANG_EN
       blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
            "%d other change%s",
-           pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField,
-           pManifest->nField==1 ? "" : "s"
+		   pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField,
+		   pManifest->nField==1 ? "" : "s"
+#endif
       );
       free(zNewStatus);
+#ifdef LANG_RU
+      blob_appendf(&brief, "Задача [%.10s]: %d изменение",
+           pManifest->zTicketUuid, pManifest->nField
+#elif LANG_EN
       blob_appendf(&brief, "Ticket [%.10s]: %d change%s",
            pManifest->zTicketUuid, pManifest->nField,
            pManifest->nField==1 ? "" : "s"
+#endif
       );
     }
   }else{
+#ifdef LANG_RU
+    blob_appendf(&comment, "Новая задача [%.10s] <i>%h</i>.",
+#elif LANG_EN
     blob_appendf(&comment, "New ticket [%.10s] <i>%h</i>.",
+#endif
       pManifest->zTicketUuid, zTitle
     );
+#ifdef LANG_RU
+    blob_appendf(&brief, "Новая задача [%.10s].", pManifest->zTicketUuid);
+#elif LANG_EN
     blob_appendf(&brief, "New ticket [%.10s].", pManifest->zTicketUuid);
+#endif
   }
   free(zTitle);
   db_multi_exec(
     "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
     "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
@@ -1771,13 +1802,21 @@
     );
     if( prior ){
       content_deltify(prior, rid, 0);
     }
     if( nWiki>0 ){
+#ifdef LANG_RU
+      zComment = mprintf("Правка страницы [%h]", p->zWikiTitle);
+#elif LANG_EN
       zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
+#endif
     }else{
+#ifdef LANG_RU
+      zComment = mprintf("Удаление страницы [%h]", p->zWikiTitle);
+#elif LANG_EN
       zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
+#endif
     }
     db_multi_exec(
       "REPLACE INTO event(type,mtime,objid,user,comment,"
       "                  bgcolor,euser,ecomment)"
       "VALUES('w',%.17g,%d,%Q,%Q,"
@@ -1869,14 +1908,23 @@
      || !validate16(p->zAttachTarget, UUID_SIZE) 
     ){
       char *zComment;
       if( p->zAttachSrc && p->zAttachSrc[0] ){
         zComment = mprintf(
+#ifdef LANG_RU
+             "Добавить вложение [%R/artifact/%S|%h] к вики-странице [%h]",
+#elif LANG_EN
              "Add attachment [%R/artifact/%S|%h] to wiki page [%h]",
+#endif
              p->zAttachSrc, p->zAttachName, p->zAttachTarget);
       }else{
-        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
+        zComment = mprintf(
+#ifdef LANG_RU
+          "Удален файл \"%h\" из страницы[%h]",
+#elif LANG_EN
+          "Delete attachment \"%h\" from wiki page [%h]",
+#endif
              p->zAttachName, p->zAttachTarget);
       }
       db_multi_exec(
         "REPLACE INTO event(type,mtime,objid,user,comment)"
         "VALUES('w',%.17g,%d,%Q,%Q)",
@@ -1885,14 +1933,23 @@
       free(zComment);
     }else{
       char *zComment;
       if( p->zAttachSrc && p->zAttachSrc[0] ){
         zComment = mprintf(
+#ifdef LANG_RU
+             "Добавить вложение [%R/artifact/%S|%h] к задаче [%S]",
+#elif LANG_EN
              "Add attachment [%R/artifact/%S|%h] to ticket [%S]",
+#endif
              p->zAttachSrc, p->zAttachName, p->zAttachTarget);
       }else{
-        zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
+        zComment = mprintf(
+#ifdef LANG_RU
+          "Удален файл \"%h\" из задачи [%.10s]",
+#elif LANG_EN
+          "Delete attachment \"%h\" from ticket [%.10s]",
+#endif
              p->zAttachName, p->zAttachTarget);
       }
       db_multi_exec(
         "REPLACE INTO event(type,mtime,objid,user,comment)"
         "VALUES('t',%.17g,%d,%Q,%Q)",

Index: src/report.c
==================================================================
--- src/report.c
+++ src/report.c
@@ -35,11 +35,17 @@
   int rn = 0;
   int cnt = 0;
 
   login_check_credentials();
   if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
-  style_header("Ticket Main Menu");
+  style_header(
+#ifdef LANG_RU
+    "Задачи: основное меню"
+#elif LANG_EN
+    "Ticket Main Menu"
+#endif
+    );
   if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
   zScript = ticket_reportlist_code();
   if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
   
   blob_zero(&ril);
@@ -996,10 +1002,11 @@
   char *zClrKey;
   int tabs;
   Stmt q;
   char *zErr1 = 0;
   char *zErr2 = 0;
+  Blob bSql = empty_blob;
 
   login_check_credentials();
   if( !g.perm.RdTkt ){ login_needed(); return; }
   rn = atoi(PD("rn","0"));
   if( rn==0 ){
@@ -1017,10 +1024,12 @@
   zTitle = db_column_malloc(&q, 0);
   zSql = db_column_malloc(&q, 1);
   zOwner = db_column_malloc(&q, 2);
   zClrKey = db_column_malloc(&q, 3);
   db_finalize(&q);
+
+
 
   if( P("order_by") ){
     /*
     ** If the user wants to do a column sort, wrap the query into a sub
     ** query and then sort the results. This is a whole lot easier than
@@ -1032,27 +1041,48 @@
       const char* zDir = PD("order_dir","");
       zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
       zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
     }
   }
+  Th_FossilInit(0, 0);
+  Th_Store("login", g.zLogin);
+  Th_Store("date", db_text(0, "SELECT datetime('now')"));
+  Th_RenderToBlob(zSql, &bSql);
+  zSql = bSql.aData;
 
   count = 0;
   if( !tabs ){
     struct GenerateHTML sState;
 
     db_multi_exec("PRAGMA empty_result_callbacks=ON");
-    style_submenu_element("Raw", "Raw", 
+    style_submenu_element(
+#ifdef LANG_RU
+      "Текстом", "Текстом", 
+#elif LANG_EN
+      "Raw", "Raw", 
+#endif
       "rptview?tablist=1&%h", PD("QUERY_STRING",""));
     if( g.perm.Admin 
        || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
-      style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
+      style_submenu_element(
+#ifdef LANG_RU
+        "Изменить", "Изменить",
+#elif LANG_EN
+        "Edit", "Edit",
+#endif
+         "rptedit?rn=%d", rn);
     }
     if( g.perm.TktFmt ){
       style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
     }
     if( g.perm.NewTkt ){
-      style_submenu_element("New Ticket", "Create a new ticket",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Новая задача", "Создать новую задачу",
+#elif LANG_EN
+        "New Ticket", "Create a new ticket",
+#endif
         "%s/tktnew", g.zTop);
     }
     style_header(zTitle);
     output_color_key(zClrKey, 1, 
         "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");

Index: src/sqlite3.c
==================================================================
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -14083,12 +14083,13 @@
     zDate++;
     neg = 1;
   }else{
     neg = 0;
   }
-  if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
-    return 1;
+  if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 &&
+    getDigits(zDate,2,1,31,'.',&D,2,1,12,'.',&M,4,0,9999,0,&Y)!=3 ){
+      return 1;
   }
   zDate += 10;
   while( sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; }
   if( parseHhMmSs(zDate, p)==0 ){
     /* We got the time */
@@ -14631,12 +14632,12 @@
 ){
   DateTime x;
   if( isDate(context, argc, argv, &x)==0 ){
     char zBuf[100];
     computeYMD_HMS(&x);
-    sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d",
-                     x.Y, x.M, x.D, x.h, x.m, (int)(x.s));
+    sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d.%02d.%04d %02d:%02d:%02d",
+                     x.D, x.M, x.Y, x.h, x.m, (int)(x.s));
     sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
   }
 }
 
 /*

Index: src/th_main.c
==================================================================
--- src/th_main.c
+++ src/th_main.c
@@ -102,10 +102,12 @@
     }
   }
   return zRc;
 }
 
+static Blob* pOutBlob = NULL;
+
 /*
 ** Send text to the appropriate output:  Either to the console
 ** or to the CGI reply buffer.  Escape all characters with special
 ** meaning to HTML if the encode parameter is true.
 */
@@ -114,11 +116,13 @@
     if( n<0 ) n = strlen(z);
     if( encode ){
       z = htmlize(z, n);
       n = strlen(z);
     }
-    if( g.cgiOutput ){
+    if(pOutBlob)
+      blob_append(pOutBlob, z, n);
+    else if( g.cgiOutput ){
       cgi_append_content(z, n);
     }else{
       fwrite(z, 1, n, stdout);
       fflush(stdout);
     }
@@ -918,10 +922,18 @@
   }else{
     sendText(z, i, 0);
   }
   return rc;
 }
+
+int Th_RenderToBlob(const char *z, Blob *pBlob){
+  int result;
+  pOutBlob = pBlob;
+  result = Th_Render(z);
+  pOutBlob = NULL;
+  return result;
+}
 
 /*
 ** COMMAND: test-th-render
 */
 void test_th_render(void){

Index: src/timeline.c
==================================================================
--- src/timeline.c
+++ src/timeline.c
@@ -820,11 +820,11 @@
   static char *zBase = 0;
   static const char zBaseSql[] =
     @ SELECT
     @   blob.rid AS blobRid,
     @   uuid AS uuid,
-    @   datetime(event.mtime,'localtime') AS timestamp,
+    @   datetime(event.mtime, 'localtime') AS timestamp,
     @   coalesce(ecomment, comment) AS comment,
     @   coalesce(euser, user) AS user,
     @   blob.rid IN leaf AS leaf,
     @   bgcolor AS bgColor,
     @   event.type AS eventType,
@@ -921,16 +921,32 @@
   blob_zero(&out);
   while( db_step(&q)==SQLITE_ROW ){
     const char *zFN = db_column_text(&q, 0);
     blob_appendf(&out, "%s%z%h</a>", zSep,
           href("%R/finfo?name=%t", zFN), zFN);
+#ifdef LANG_RU
+    zSep = " или ";
+#elif LANG_EN
     zSep = " or ";
+#endif
   }
   db_finalize(&q);
   return blob_str(&out);
 }
 
+#ifdef LANG_RU
+const char* numberSuffix(int number, const char* s1, const char* s24, const char* s590){
+	int lastDigit = number % 10;
+	if(lastDigit == 1 && number != 11)
+		return s1;
+	if(lastDigit >= 2 && lastDigit <= 4 && (number < 12 || number > 14))
+		return s24;
+	return s590;
+}
+#elif LANG_EN
+#define numberSuffix(n) (n==1?"":"s")
+#endif
 
 /*
 ** WEBPAGE: timeline
 **
 ** Query parameters:
@@ -1045,11 +1061,17 @@
     }else{
       zUses = 0;
     }
   }
 
-  style_header("Timeline");
+  style_header(
+#ifdef LANG_RU
+    "События"
+#elif LANG_EN
+    "Timeline"
+#endif
+    );
   login_anonymous_available();
   timeline_temp_table();
   blob_zero(&sql);
   blob_zero(&desc);
   blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
@@ -1082,13 +1104,25 @@
       blob_appendf(&sql, ",%d", p->rid);
       p = p->u.pTo;
     }
     blob_append(&sql, ")", -1);
     path_reset();
-    blob_append(&desc, "All nodes on the path from ", -1);
+    blob_append(&desc,
+#ifdef LANG_RU
+      "Все узлы с путём от ",
+#elif LANG_EN
+      "All nodes on the path from ",
+#endif
+      -1);
     blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom);
-    blob_append(&desc, " and ", -1);
+    blob_append(&desc,
+#ifdef LANG_RU
+      " до "
+#elif LANG_EN
+      " and "
+#endif
+      , -1);
     blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
     tmFlags |= TIMELINE_DISJOINT;
     db_multi_exec("%s", blob_str(&sql));
   }else if( (p_rid || d_rid) && g.perm.Read ){
     /* If p= or d= is present, ignore all other parameters other than n= */
@@ -1108,26 +1142,48 @@
     nd = 0;
     if( d_rid ){
       compute_descendants(d_rid, nEntry+1);
       nd = db_int(0, "SELECT count(*)-1 FROM ok");
       if( nd>=0 ) db_multi_exec("%s", blob_str(&sql));
-      if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
+      if( nd>0 )
+        blob_appendf(&desc,
+#ifdef LANG_RU
+          "%d потом%s", nd, numberSuffix(nd, "ок", "ка", "ков"));
+#elif LANG_EN
+          "%d descendant%s", nd, numberSuffix(nd));
+#endif
       if( useDividers ) timeline_add_dividers(0, d_rid);
       db_multi_exec("DELETE FROM ok");
     }
     if( p_rid ){
       compute_ancestors(p_rid, nEntry+1, 0);
       np = db_int(0, "SELECT count(*)-1 FROM ok");
       if( np>0 ){
-        if( nd>0 ) blob_appendf(&desc, " and ");
-        blob_appendf(&desc, "%d ancestors", np);
+        if( nd>0 ) blob_appendf(&desc,
+#ifdef LANG_RU
+          " и "
+#elif LANG_EN
+          " and "
+#endif
+          );
+        blob_appendf(&desc,
+#ifdef LANG_RU
+          "%d пред%s", np, numberSuffix(np, "ок", "ка", "ков"));
+#elif LANG_EN
+          "%d ancestor%s", numberSuffix(np));
+#endif
         db_multi_exec("%s", blob_str(&sql));
       }
       if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid);
     }
-    blob_appendf(&desc, " of %z[%.10s]</a>",
-                   href("%R/info/%s", zUuid), zUuid);
+    blob_appendf(&desc,
+#ifdef LANG_RU
+      " %z[%.10s]</a>",
+#elif LANG_EN
+      " of %z[%.10s]</a>",
+#endif
+         href("%R/info/%s", zUuid), zUuid);
   }else if( f_rid && g.perm.Read ){
     /* If f= is present, ignore all other parameters other than n= */
     char *zUuid;
     db_multi_exec(
        "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
@@ -1137,18 +1193,29 @@
        f_rid, f_rid, f_rid
     );
     blob_appendf(&sql, " AND event.objid IN ok");
     db_multi_exec("%s", blob_str(&sql));
     if( useDividers ) timeline_add_dividers(0, f_rid);
-    blob_appendf(&desc, "Parents and children of check-in ");
+    blob_appendf(&desc,
+#ifdef LANG_RU
+      "Предки и потомки фиксации "
+#elif LANG_EN
+      "Parents and children of check-in "
+#endif
+      );
     zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
     blob_appendf(&desc, "%z[%.10s]</a>", href("%R/info/%s", zUuid), zUuid);
     tmFlags |= TIMELINE_DISJOINT;
   }else{
     /* Otherwise, a timeline based on a span of time */
     int n;
-    const char *zEType = "timeline item";
+    const char *zEType =
+#ifdef LANG_RU
+      "событие";
+#elif LANG_EN
+      "timeline item";
+#endif
     char *zDate;
     char *zNEntry = mprintf("%d", nEntry);
     url_add_parameter(&url, "n", zNEntry);
     if( zUses ){
       blob_appendf(&sql, " AND event.objid IN usesfile ");
@@ -1281,88 +1348,204 @@
     }
     blob_appendf(&sql, " LIMIT %d", nEntry);
     db_multi_exec("%s", blob_str(&sql));
 
     n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
-    if( zAfter==0 && zBefore==0 && zCirca==0 ){
-      blob_appendf(&desc, "%d most recent %ss", n, zEType);
-    }else{
-      blob_appendf(&desc, "%d %ss", n, zEType);
+    {
+#ifdef LANG_RU
+      const char* rZEtype;
+      const char* rSuffix;
+      const char* rLastTitleSuffix = numberSuffix(n, "яя", "ие", "их");
+      switch(zEType[0]){
+          case 'c': /*check-in*/
+            rZEtype = "фиксаци";
+            rSuffix = numberSuffix(n, "я", "и", "й");
+            break;
+          case 'w': /*wiki-edit*/
+            rZEtype = "вики-прав";
+            rSuffix = numberSuffix(n, "ка", "ки", "ок");
+            break;
+          case 't': /*tiket-change*/
+            rZEtype = "задач";
+            rSuffix = numberSuffix(n, "а", "и", "");
+            break;
+          case 'e': /*events*/
+            rZEtype = "новост";
+            rSuffix = numberSuffix(n, "ь", "и", "ей");
+            break;
+          default:
+            rZEtype = "событи";
+            rSuffix = numberSuffix(n, "е", "я", "й");
+            rLastTitleSuffix = numberSuffix(n, "ее", "их", "их");
+      }
+#endif
+      if( zAfter==0 && zBefore==0 && zCirca==0 ){
+ #ifdef LANG_RU
+        blob_appendf(&desc, "%d последн%s %s%s", n, rLastTitleSuffix, rZEtype, rSuffix);
+#elif LANG_EN
+        blob_appendf(&desc, "%d most recent %ss", n, zEType);
+#endif
+      }else{
+#ifdef LANG_RU
+        blob_appendf(&desc, "%d %s%s", n, rZEtype, rSuffix);
+#elif LANG_EN
+        blob_appendf(&desc, "%d %ss", n, zEType);
+#endif
+      }
     }
     if( zUses ){
       char *zFilenames = names_of_file(zUses);
       blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames,
                    href("%R/artifact/%S",zUses), zUses);
       tmFlags |= TIMELINE_DISJOINT;
     }
     if( zUser ){
+#ifdef LANG_RU
+      blob_appendf(&desc, " пользователя %h", zUser);
+#elif LANG_EN
       blob_appendf(&desc, " by user %h", zUser);
+#endif
       tmFlags |= TIMELINE_DISJOINT;
     }
     if( zTagName ){
+#ifdef LANG_RU
+      blob_appendf(&desc, " с тэгом \"%h\"", zTagName);
+#elif LANG_EN
       blob_appendf(&desc, " tagged with \"%h\"", zTagName);
+#endif
       tmFlags |= TIMELINE_DISJOINT;
     }else if( zBrName ){
+#ifdef LANG_RU
+      blob_appendf(&desc, " ветки \"%h\"", zBrName);
+#elif LANG_EN
       blob_appendf(&desc, " related to \"%h\"", zBrName);
+#endif
       tmFlags |= TIMELINE_DISJOINT;
     }
     if( rAfter>0.0 ){
       if( rBefore>0.0 ){
-        blob_appendf(&desc, " occurring between %h and %h.<br>",
-                     zAfter, zBefore);
+        blob_appendf(&desc,
+#ifdef LANG_RU
+			" между %h and %h.<br>"
+#elif LANG_EN
+			" occurring between %h and %h.<br>"
+#endif
+			, zAfter, zBefore);
       }else{
-        blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
+        blob_appendf(&desc,
+#ifdef LANG_RU
+			" после %h.<br />"
+#elif LANG_EN
+			" occurring on or after %h.<br />"
+#endif
+			, zAfter);
       }
     }else if( rBefore>0.0 ){
+#ifdef LANG_RU
+      blob_appendf(&desc, " до %h.<br />", zBefore);
+#elif LANG_EN
       blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
+#endif
     }else if( rCirca>0.0 ){
+#ifdef LANG_RU
+      blob_appendf(&desc, " около %h.<br />", zCirca);
+#elif LANG_EN
       blob_appendf(&desc, " occurring around %h.<br />", zCirca);
+#endif
     }
     if( zSearch ){
+#ifdef LANG_RU
+      blob_appendf(&desc, " похожие на \"%h\"", zSearch);
+#elif LANG_EN
       blob_appendf(&desc, " matching \"%h\"", zSearch);
+#endif
     }
     if( g.perm.Hyperlink ){
       if( zAfter || n==nEntry ){
         zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
+#ifdef LANG_RU
+        timeline_submenu(&url, "Ранее", "b", zDate, "a");
+#elif LANG_EN
         timeline_submenu(&url, "Older", "b", zDate, "a");
+#endif
         free(zDate);
       }
       if( zBefore || (zAfter && n==nEntry) ){
         zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
+#ifdef LANG_RU
+        timeline_submenu(&url, "Новее", "a", zDate, "b");
+#elif LANG_EN
         timeline_submenu(&url, "Newer", "a", zDate, "b");
+#endif
         free(zDate);
       }else if( tagid==0 ){
         if( zType[0]!='a' ){
+#ifdef LANG_RU
+          timeline_submenu(&url, "Все типы", "y", "all", 0);
+#elif LANG_EN
           timeline_submenu(&url, "All Types", "y", "all", 0);
+#endif
         }
         if( zType[0]!='w' && g.perm.RdWiki ){
+#ifdef LANG_RU
+          timeline_submenu(&url, "Только вики-правки", "y", "w", 0);
+#elif LANG_EN
           timeline_submenu(&url, "Wiki Only", "y", "w", 0);
+#endif
         }
         if( zType[0]!='c' && g.perm.Read ){
+#ifdef LANG_RU
+          timeline_submenu(&url, "Только фиксации", "y", "ci", 0);
+#elif LANG_EN
           timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
+#endif
         }
         if( zType[0]!='t' && g.perm.RdTkt ){
+#ifdef LANG_RU
+          timeline_submenu(&url, "Только задачи", "y", "t", 0);
+#elif LANG_EN
           timeline_submenu(&url, "Tickets Only", "y", "t", 0);
+#endif
         }
         if( zType[0]!='e' && g.perm.RdWiki ){
+#ifdef LANG_RU
+          timeline_submenu(&url, "Только новости", "y", "e", 0);
+#elif LANG_EN
           timeline_submenu(&url, "Events Only", "y", "e", 0);
+#endif
         }
         if( zType[0]!='g' && g.perm.Read ){
           timeline_submenu(&url, "Tags Only", "y", "g", 0);
         }
       }
       if( nEntry>20 ){
+#ifdef LANG_RU
+        timeline_submenu(&url, "20 событий", "n", "20", 0);
+#elif LANG_EN
         timeline_submenu(&url, "20 Entries", "n", "20", 0);
+#endif
       }
       if( nEntry<200 ){
+#ifdef LANG_RU
+        timeline_submenu(&url, "200 событий", "n", "200", 0);
+#elif LANG_EN
         timeline_submenu(&url, "200 Entries", "n", "200", 0);
+#endif
       }
       if( zType[0]=='a' || zType[0]=='c' ){
         if( tmFlags & TIMELINE_FCHANGES ){
+#ifdef LANG_RU
+          timeline_submenu(&url, "Не показывать файлы", "fc", 0, 0);
+#elif LANG_EN
           timeline_submenu(&url, "Hide Files", "fc", 0, 0);
+#endif
         }else{
+#ifdef LANG_RU
+          timeline_submenu(&url, "Показывать файлы", "fc", "", 0);
+#elif LANG_EN
           timeline_submenu(&url, "Show Files", "fc", "", 0);
+#endif
         }
       }
     }
   }
   if( P("showsql") ){

Index: src/tkt.c
==================================================================
--- src/tkt.c
+++ src/tkt.c
@@ -422,37 +422,82 @@
   const char *zUuid = PD("name","");
 
   login_check_credentials();
   if( !g.perm.RdTkt ){ login_needed(); return; }
   if( g.perm.WrTkt || g.perm.ApndTkt ){
-    style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
+    style_submenu_element(
+#ifdef LANG_RU
+      "Правка", "Изменить задачу",
+#elif LANG_EN
+      "Edit", "Edit The Ticket",
+#endif
+      "%s/tktedit?name=%T",
         g.zTop, PD("name",""));
   }
   if( g.perm.Hyperlink ){
-    style_submenu_element("History", "History Of This Ticket", 
+    style_submenu_element(
+#ifdef LANG_RU
+      "История", "Исория задачи", 
+#elif LANG_EN
+      "History", "History Of This Ticket", 
+#endif
         "%s/tkthistory/%T", g.zTop, zUuid);
-    style_submenu_element("Timeline", "Timeline Of This Ticket", 
+    style_submenu_element(
+#ifdef LANG_RU
+      "События", "События по этой задаче", 
+#elif LANG_EN
+      "Timeline", "Timeline Of This Ticket", 
+#endif
         "%s/tkttimeline/%T", g.zTop, zUuid);
-    style_submenu_element("Check-ins", "Check-ins Of This Ticket", 
+    style_submenu_element(
+#ifdef LANG_RU
+      "Фиксации", "Фиксации по этой задаче", 
+#elif LANG_EN
+      "Check-ins", "Check-ins Of This Ticket", 
+#endif
         "%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
   }
   if( g.perm.NewTkt ){
-    style_submenu_element("New Ticket", "Create a new ticket",
+    style_submenu_element(
+#ifdef LANG_RU
+      "Новая задача", "Создать новую задачу",
+#elif LANG_EN
+      "New Ticket", "Create a new ticket",
+#endif
         "%s/tktnew", g.zTop);
   }
   if( g.perm.ApndTkt && g.perm.Attach ){
-    style_submenu_element("Attach", "Add An Attachment",
+    style_submenu_element(
+#ifdef LANG_RU
+      "Добавить файл", "Добавить файл к задаче",
+#elif LANG_EN
+      "Attach", "Add An Attachment",
+#endif
         "%s/attachadd?tkt=%T&from=%s/tktview/%t",
         g.zTop, zUuid, g.zTop, zUuid);
   }
   if( P("plaintext") ){
+#ifdef LANG_RU
+    style_submenu_element("Форматировано", "Форматировано", "%R/tktview/%S", zUuid);
+#elif LANG_EN
     style_submenu_element("Formatted", "Formatted", "%R/tktview/%S", zUuid);
+#endif
   }else{
+#ifdef LANG_RU
+    style_submenu_element("Текстом", "Текстом",
+#elif LANG_EN
     style_submenu_element("Plaintext", "Plaintext",
+#endif
                           "%R/tktview/%S?plaintext", zUuid);
   }
-  style_header("View Ticket");
+  style_header(
+#ifdef LANG_RU
+	  "Просмотр задачи"
+#elif LANG_EN
+	  "View Ticket"
+#endif
+	  );
   if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
   ticket_init();
   initializeVariablesFromCGI();
   getAllTicketFields();
   initializeVariablesFromDb();
@@ -464,11 +509,15 @@
 
   zFullName = db_text(0, 
        "SELECT tkt_uuid FROM ticket"
        " WHERE tkt_uuid GLOB '%q*'", zUuid);
   if( zFullName ){
+#ifdef LANG_RU
+    attachment_list(zFullName, "<hr /><h2>Вложения:</h2><ul>");
+#elif LANG_EN
     attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
+#endif
   }
  
   style_footer();
 }
 
@@ -560,11 +609,15 @@
   int nJ = 0;
   Blob tktchng, cksum;
 
   login_verify_csrf_secret();
   if( !captcha_is_correct() ){
+#ifdef LANG_RU
+    @ <p class="generalError">Ошибка: неверный код.</p>
+#elif LANG_EN
     @ <p class="generalError">Error: Incorrect security code.</p>
+#endif
     return TH_OK;
   }
   zUuid = (const char *)pUuid;
   blob_zero(&tktchng);
   zDate = date_in_standard_format("now");
@@ -581,10 +634,16 @@
     const char *zValue;
     int nValue;
     if( aField[i].zAppend ) continue;
     zValue = Th_Fetch(aField[i].zName, &nValue);
     if( zValue ){
+      const char* zTemp = wiki_sanify(zValue);
+      if(zTemp != zValue)
+      {
+        zValue = zTemp;
+        nValue = strlen(zValue);
+      }
       while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
       if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
        || memcmp(zValue, aField[i].zValue, nValue)!=0
        || strlen(aField[i].zValue)!=nValue
       ){
@@ -652,11 +711,17 @@
   login_check_credentials();
   if( !g.perm.NewTkt ){ login_needed(); return; }
   if( P("cancel") ){
     cgi_redirect("home");
   }
-  style_header("New Ticket");
+  style_header(
+#ifdef LANG_RU
+    "Новая задача"
+#elif LANG_EN
+    "New Ticket"
+#endif
+    );
   if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
   ticket_init();
   initializeVariablesFromCGI();
   getAllTicketFields();
   initializeVariablesFromDb();
@@ -703,26 +768,44 @@
   if( !g.perm.ApndTkt && !g.perm.WrTkt ){ login_needed(); return; }
   zName = P("name");
   if( P("cancel") ){
     cgi_redirectf("tktview?name=%T", zName);
   }
-  style_header("Edit Ticket");
+  style_header(
+#ifdef LANG_RU
+    "Изменение задачи"
+#elif LANG_EN
+    "Edit Ticket"
+#endif
+    );
   if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
           || !validate16(zName,nName) ){
-    @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
+#ifdef LANG_RU
+          @ <span class="tktError">Неверный ID задачи: \"%h(zName)\"</span>
+#elif LANG_EN
+          @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
+#endif
     style_footer();
     return;
   }
   nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
                 zName);
   if( nRec==0 ){
+#ifdef LANG_RU
+    @ <span class="tktError">Не найдена задача: \"%h(zName)\"</span>
+#elif LANG_EN
     @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
+#endif
     style_footer();
     return;
   }
   if( nRec>1 ){
+#ifdef LANG_RU
+    @ <span class="tktError">Несколько задач (%d(nRec)) начинаются с:
+#elif LANG_EN
     @ <span class="tktError">%d(nRec) tickets begin with:
+#endif
     @ \"%h(zName)\"</span>
     style_footer();
     return;
   }
   if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
@@ -803,33 +886,69 @@
   login_check_credentials();
   if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(); return; }
   zUuid = PD("name","");
   zType = PD("y","a");
   if( zType[0]!='c' ){
-    style_submenu_element("Check-ins", "Check-ins",
+    style_submenu_element(
+#ifdef LANG_RU
+      "Фиксации", "Фиксации",
+#elif LANG_EN
+      "Check-ins", "Check-ins",
+#endif
        "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
   }else{
-    style_submenu_element("Timeline", "Timeline",
+    style_submenu_element(
+#ifdef LANG_RU
+      "События", "События",
+#elif LANG_EN
+      "Timeline", "Timeline",
+#endif
        "%s/tkttimeline?name=%T", g.zTop, zUuid);
   }
-  style_submenu_element("History", "History",
+  style_submenu_element(
+#ifdef LANG_RU
+    "История", "История",
+#elif LANG_EN
+    "History", "History",
+#endif
     "%s/tkthistory/%s", g.zTop, zUuid);
-  style_submenu_element("Status", "Status",
+  style_submenu_element(
+#ifdef LANG_RU
+    "Статус", "Статус",
+#elif LANG_EN
+    "Status", "Status",
+#endif
     "%s/info/%s", g.zTop, zUuid);
   if( zType[0]=='c' ){
-    zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
+    zTitle = mprintf(
+#ifdef LANG_RU
+      "Фиксации, связанные с задачей %h",
+#elif LANG_EN
+      "Check-Ins Associated With Ticket %h",
+#endif
+      zUuid);
   }else{
-    zTitle = mprintf("Timeline Of Ticket %h", zUuid);
+    zTitle = mprintf(
+#ifdef LANG_RU
+      "События, связанные с задачей %h",
+#elif LANG_EN
+      "Timeline Of Ticket %h",
+#endif
+      zUuid);
   }
   style_header(zTitle);
   free(zTitle);
 
   sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
   canonical16(zGlobPattern, strlen(zGlobPattern));
   tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
   if( tagid==0 ){
+#ifdef LANG_RU
+    @ Задача %h(zUuid) не найдена.
+#elif LANG_EN
     @ No such ticket: %h(zUuid)
+#endif
     style_footer();
     return;
   }
   zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
                          tagid);
@@ -876,16 +995,35 @@
   int nChng = 0;
 
   login_check_credentials();
   if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(); return; }
   zUuid = PD("name","");
-  zTitle = mprintf("History Of Ticket %h", zUuid);
-  style_submenu_element("Status", "Status",
+  zTitle = mprintf(
+#ifdef LANG_RU
+    "История задачи %h",
+#elif LANG_EN
+    "History Of Ticket %h",
+#endif
+    zUuid);
+  style_submenu_element(
+#ifdef LANG_RU
+    "Статус", "Статус",
+#elif LANG_EN
+    "Status", "Status",
+#endif
     "%s/info/%s", g.zTop, zUuid);
-  style_submenu_element("Check-ins", "Check-ins",
+#ifdef LANG_RU
+    style_submenu_element("Фиксации", "Фиксации",
+#elif LANG_EN
+    style_submenu_element("Check-ins", "Check-ins",
+#endif
     "%s/tkttimeline?name=%s&y=ci", g.zTop, zUuid);
-  style_submenu_element("Timeline", "Timeline",
+#ifdef LANG_RU
+    style_submenu_element("События", "События",
+#elif LANG_EN
+    style_submenu_element("Timeline", "Timeline",
+#endif
     "%s/tkttimeline?name=%s", g.zTop, zUuid);
   if( P("plaintext")!=0 ){
     style_submenu_element("Formatted", "Formatted",
                           "%R/tkthistory/%S", zUuid);
   }else{
@@ -895,11 +1033,15 @@
   style_header(zTitle);
   free(zTitle);
 
   tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
   if( tagid==0 ){
+#ifdef LANG_RU
+    @ Задача %h(zUuid) не найдена
+#elif LANG_EN
     @ No such ticket: %h(zUuid)
+#endif
     style_footer();
     return;
   }
   db_prepare(&q,
     "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
@@ -930,28 +1072,48 @@
     if( zFile!=0 ){
       const char *zSrc = db_column_text(&q, 3);
       const char *zUser = db_column_text(&q, 5);
       if( zSrc==0 || zSrc[0]==0 ){
         @ 
+#ifdef LANG_RU
+        @ <li>Удалено вложение "%h(zFile)"
+#elif LANG_EN
         @ <li><p>Delete attachment "%h(zFile)"
+#endif
       }else{
         @ 
+#ifdef LANG_RU
+        @ <li><p>Добавлено вложение
+#elif LANG_EN
         @ <li><p>Add attachment
-        @ "%z(href("%R/artifact/%S",zSrc))%h(zFile)</a>"
+#endif
+		@ "%z(href("%R/artifact/%S",zSrc))%h(zFile)</a>"
       }
       @ [%z(href("%R/artifact/%T",zChngUuid))%s(zShort)</a>]
+#ifdef LANG_RU
+      @ (rid %d(rid)) пользователем
+      hyperlink_to_user(zUser,zDate," ");
+#elif LANG_EN
       @ (rid %d(rid)) by
       hyperlink_to_user(zUser,zDate," on");
+#endif
       hyperlink_to_date(zDate, ".</p>");
     }else{
       pTicket = manifest_get(rid, CFTYPE_TICKET);
       if( pTicket ){
         @
+#ifdef LANG_RU
+        @ <li>Задача изменена
+        @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
+        @ (rid %d(rid)) пользователем
+        hyperlink_to_user(pTicket->zUser,zDate," ");
+#elif LANG_EN
         @ <li><p>Ticket change
         @ [%z(href("%R/artifact/%T",zChngUuid))%s(zShort)</a>]
         @ (rid %d(rid)) by
         hyperlink_to_user(pTicket->zUser,zDate," on");
+#endif
         hyperlink_to_date(zDate, ":");
         @ </p>
         ticket_output_change_artifact(pTicket, "a");
       }
       manifest_destroy(pTicket);

Index: src/url.c
==================================================================
--- src/url.c
+++ src/url.c
@@ -380,18 +380,29 @@
 ** Prompt the user for the password for g.urlUser.  Store the result
 ** in g.urlPasswd.
 */
 void url_prompt_for_password(void){
   if( isatty(fileno(stdin)) ){
-    char *zPrompt = mprintf("\rpassword for %s: ", g.urlUser);
+    char *zPrompt = mprintf(
+#ifdef LANG_RU
+		"\rПароль для %s: ",
+#elif LANG_EN
+		"\rpassword for %s: ",
+#endif
+		g.urlUser);
     Blob x;
     prompt_for_password(zPrompt, &x, 0);
     free(zPrompt);
     g.urlPasswd = mprintf("%b", &x);
     blob_reset(&x);
   }else{
-    fossil_fatal("missing or incorrect password for user \"%s\"",
+    fossil_fatal(
+#ifdef LANG_RU
+		"Отсутствует или неверен пароль для пользователя \"%s\"",
+#elif LANG_EN
+		"missing or incorrect password for user \"%s\"",
+#endif
                  g.urlUser);
   }
 }
 
 /* Preemptively prompt for a password if a username is given in the

Index: src/user.c
==================================================================
--- src/user.c
+++ src/user.c
@@ -49,12 +49,11 @@
 ** getpass for Windows
 */
 static char *getpass(const char *prompt){
   static char pwd[64];
   size_t i;
-
-  fputs(prompt,stderr);
+  fossil_puts(prompt,1);
   fflush(stderr);
   for(i=0; i<sizeof(pwd)-1; ++i){
     pwd[i] = _getch();
     if(pwd[i]=='\r' || pwd[i]=='\n'){
       break;
@@ -115,13 +114,25 @@
   blob_zero(&secondTry);
   while(1){
     prompt_for_passphrase(zPrompt, pPassphrase);
     if( verify==0 ) break;
     if( verify==1 && blob_size(pPassphrase)==0 ) break;
-    prompt_for_passphrase("Retype new password: ", &secondTry);
+    prompt_for_passphrase(
+#ifdef LANG_RU
+		"��������� ����� ������: ",
+#elif LANG_EN
+		"Retype new password: ",
+#endif
+		&secondTry);
     if( blob_compare(pPassphrase, &secondTry) ){
-      fossil_print("Passphrases do not match.  Try again...\n");
+      fossil_print(
+#ifdef LANG_RU
+		  "������ �� ���������. ���������� ��� ���...\n"
+#elif LANG_EN
+		  "Passphrases do not match.  Try again...\n"
+#endif
+		  );
     }else{
       break;
     }
   }
   blob_reset(&secondTry);

Index: src/wiki.c
==================================================================
--- src/wiki.c
+++ src/wiki.c
@@ -47,28 +47,44 @@
 
 /*
 ** Output rules for well-formed wiki pages
 */
 static void well_formed_wiki_name_rules(void){
+#ifdef LANG_RU
+  @ <ul>
+    @ <li>Не должно начинаться или заканчиваться пробелом.</li>
+    @ <li> Не должно содержать управляющих символов, включая табуляцию 
+    @      и перевод строки.</li>
+    @ <li> Не должно быть двух и более пробелов подряд.</li>
+    @ <li> Длина от 3 до 100 символов.</li>
+    @ </ul>
+#elif LANG_EN
   @ <ul>
   @ <li> Must not begin or end with a space.</li>
   @ <li> Must not contain any control characters, including tab or
   @      newline.</li>
   @ <li> Must not have two or more spaces in a row internally.</li>
   @ <li> Must be between 3 and 100 characters in length.</li>
   @ </ul>
+#endif
 }
 
 /*
 ** Check a wiki name.  If it is not well-formed, then issue an error
 ** and return true.  If it is well-formed, return false.
 */
 static int check_name(const char *z){
   if( !wiki_name_is_wellformed((const unsigned char *)z) ){
+#ifdef LANG_RU
+    style_header("Ошибка в названии страницы");
+    @ "<span class="wikiError">%h(z)</span>" не может быть названием страницы.
+    @ Правила для названий страниц:
+#elif LANG_EN
     style_header("Wiki Page Name Error");
     @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
     @ Rules for wiki page names:
+#endif
     well_formed_wiki_name_rules();
     style_footer();
     return 1;
   }
   return 0;
@@ -101,17 +117,27 @@
     cgi_set_parameter_nocopy("name", g.zExtra);
     g.isHome = 1;
     wiki_page();
     return;
   }
+#ifdef LANG_RU
+  style_header("Домашняя страница");
+  @ <p>Это фиктивная домашняя страница проекта.
+  @ Для заполнения этой страницы сначала перейдите в
+  @ <a href="%s(g.zTop)/setup_config">setup/config</a>
+  @ и задайте имя проекта. Затем создайте страницу с
+  @ таким же названием. Содержимое созданной страницы
+  @ будет отображаться вместо этого сообщения.</p>
+#elif LANG_EN
   style_header("Home");
   @ <p>This is a stub home-page for the project.
   @ To fill in this page, first go to
   @ %z(href("%R/setup_config"))setup/config</a>
   @ and establish a "Project Name".  Then create a
   @ wiki page with that name.  The content of that wiki page
   @ will be displayed in place of this message.</p>
+#endif
   style_footer();
 }
 
 /*
 ** Return true if the given pagename is the name of the sandbox
@@ -131,43 +157,77 @@
   int isSandbox;
   char *zUuid;
   Blob wiki;
   Manifest *pWiki = 0;
   const char *zPageName;
+#ifdef LANG_RU
+  char *zBody = mprintf("%s","<i>Пустая страница</i>");
+#elif LANG_EN
   char *zBody = mprintf("%s","<i>Empty Page</i>");
-
+#endif
   login_check_credentials();
   if( !g.perm.RdWiki ){ login_needed(); return; }
   zPageName = P("name");
   if( zPageName==0 ){
+#ifdef LANG_RU
+    style_header("Вики");
+    @ <ul>
+#elif LANG_EN
     style_header("Wiki");
     @ <ul>
+#endif
     { char *zHomePageName = db_get("project-name",0);
       if( zHomePageName ){
         @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
+#ifdef LANG_RU
+        @      %h(zHomePageName)</a> - домашняя страница.</li>
+#elif LANG_EN
         @      %h(zHomePageName)</a> wiki home page.</li>
+#endif
       }
     }
+#ifdef LANG_RU
+    @ <li> %z(href("%R/timeline?y=w"))Последние правки</a></li>
+    @ <li> %z(href("%R/wiki_rules"))Правила форматирования</a></li>
+    @ <li> Используйте %z(href("%R/wiki?name=Sandbox"))песочницу</a> для экспериментов.</li>
+#elif LANG_EN
     @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
     @ <li> %z(href("%R/wiki_rules"))Formatting rules</a> for wiki.</li>
     @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
     @      to experiment.</li>
+#endif
     if( g.perm.NewWiki ){
+#ifdef LANG_RU
+      @ <li> Создать %z(href("%R/wikinew"))новую страницу</a>.</li>
+#elif LANG_EN
       @ <li>  Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
+#endif
       if( g.perm.Write ){
+#ifdef LANG_RU
+        @ <li> Создать %z(href("%R/eventedit"))новое событие</a>.</li>
+#elif LANG_EN
         @ <li>   Create a %z(href("%R/eventedit"))new event</a>.</li>
+#endif
       }
     }
+#ifdef LANG_RU
+    @ <li> %z(href("%R/wcontent"))Все страницы</a>
+    @      на сервере.</li>
+    @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
+    @     Поиск по названиям: <input type="text" name="title"/>
+    @  &nbsp; <input type="submit" value = "Искать" /></div></form>
+#elif LANG_EN
     @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
     @      available on this server.</li>
     if( g.perm.ModWiki ){
       @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
     }
     @ <li>
     form_begin(0, "%R/wfind");
     @  <div>Search wiki titles: <input type="text" name="title"/>
     @  &nbsp; <input type="submit" /></div></form>
+#endif
     @ </li>
     @ </ul>
     style_footer();
     return;
   }
@@ -197,30 +257,57 @@
       style_submenu_element("Details", "Details",
                    "%R/info/%S", zUuid);
     }
     if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
       if( db_get_boolean("wysiwyg-wiki", 0) ){
-        style_submenu_element("Edit", "Edit Wiki Page",
-             "%s/wikiedit?name=%T&wysiwyg=1",
+        style_submenu_element(
+#ifdef LANG_RU
+			"Правка", "Редактировать вики-страницу"
+#elif LANG_EN
+			"Edit", "Edit Wiki Page"
+#endif
+			, "%s/wikiedit?name=%T&wysiwyg=1",
              g.zTop, zPageName);
       }else{
-        style_submenu_element("Edit", "Edit Wiki Page",
-             "%s/wikiedit?name=%T",
+        style_submenu_element(
+#ifdef LANG_RU
+			"Правка", "Редактировать вики-страницу"
+#elif LANG_EN
+			"Edit", "Edit Wiki Page"
+#endif
+			, "%s/wikiedit?name=%T",
              g.zTop, zPageName);
       }
     }
     if( rid && g.perm.ApndWiki && g.perm.Attach ){
-      style_submenu_element("Attach", "Add An Attachment",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Добавить файл", "Добавить файл к странице",
+#elif LANG_EN
+        "Attach", "Add An Attachment",
+#endif
            "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
            g.zTop, zPageName, g.zTop, zPageName);
     }
     if( rid && g.perm.ApndWiki ){
-      style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
+      style_submenu_element(
+#ifdef LANG_RU
+        "Комментировать", "Добавить комментарий к странице", 
+#elif LANG_EN
+        "Append", "Add A Comment", 
+#endif
+        "%s/wikiappend?name=%T",
            g.zTop, zPageName);
     }
     if( g.perm.Hyperlink ){
-      style_submenu_element("History", "History", "%s/whistory?name=%T",
+      style_submenu_element(
+#ifdef LANG_RU
+        "История", "Показать историю правок",
+#elif LANG_EN
+        "History", "History",
+#endif
+        "%s/whistory?name=%T",
            g.zTop, zPageName);
     }
   }
   style_set_current_page("%s?name=%T", g.zPath, zPageName);
   style_header(zPageName);
@@ -453,19 +540,19 @@
   const char *zRemark;
   char *zId;
 
   zDate = db_text(0, "SELECT datetime('now')");
   zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
-  blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", 
+  blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>%s %h", 
     zId, zDate, g.zLogin);
   free(zDate);
   zUser = PD("u",g.zLogin);
   if( zUser[0] && fossil_strcmp(zUser,g.zLogin) ){
-    blob_appendf(p, " (claiming to be %h)", zUser);
+    blob_appendf(p, ", представившись как %h,", zUser);
   }
-  zRemark = PD("r","");
-  blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
+  zRemark = wiki_sanify(PD("r",""));
+  blob_appendf(p, " добавил:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
 }
 
 /*
 ** WEBPAGE: wikiappend
 ** URL: /wikiappend?name=PAGENAME
@@ -706,16 +793,30 @@
   Stmt q;
   int showAll = P("all")!=0;
 
   login_check_credentials();
   if( !g.perm.RdWiki ){ login_needed(); return; }
+#ifdef LANG_RU
+  style_header("Доступные вики-стариницы");
+#elif LANG_EN
   style_header("Available Wiki Pages");
+#endif
+  
   if( showAll ){
+#ifdef LANG_RU
+    style_submenu_element("Активные", "Только активные страницы", "%s/wcontent", g.zTop);
+#elif LANG_EN
     style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
+#endif
   }else{
+#ifdef LANG_RU
+    style_submenu_element("Все", "Все страницы", "%s/wcontent?all=1", g.zTop);
+#elif LANG_EN
     style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
+#endif
   }
+  
   @ <ul>
   wiki_prepare_page_list(&q);
   while( db_step(&q)==SQLITE_ROW ){
     const char *zName = db_column_text(&q, 0);
     int size = db_column_int(&q, 1);

Index: src/wikiformat.c
==================================================================
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -234,11 +234,11 @@
 #define MUTYPE_HYPERLINK   0x0400   /* <a> */
 
 /*
 ** These markup types must have an end tag.
 */
-#define MUTYPE_STACK  (MUTYPE_BLOCK | MUTYPE_FONT | MUTYPE_LIST | MUTYPE_TABLE)
+#define MUTYPE_STACK  (MUTYPE_BLOCK | MUTYPE_LIST | MUTYPE_TABLE)
 
 /*
 ** This markup types are allowed for "inline" text.
 */
 #define MUTYPE_INLINE (MUTYPE_FONT | MUTYPE_HYPERLINK)
@@ -381,10 +381,11 @@
 #define TOKEN_NUM_LI        7  /*  "  #  " */
 #define TOKEN_ENUM          8  /*  "  \(?\d+[.)]?  " */
 #define TOKEN_INDENT        9  /*  "   " */
 #define TOKEN_RAW           10 /* Output exactly (used when wiki-use-html==1) */
 #define TOKEN_TEXT          11 /* None of the above */
+#define TOKEN_SWHTML        12 /* switch use html */
 
 /*
 ** State flags.  Save the lower 16 bits for the WIKI_* flags.
 */
 #define AT_NEWLINE          0x0010000  /* At start of a line */
@@ -632,17 +633,21 @@
 static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
   int n;
   if( z[0]=='<' ){
     n = markupLength(z);
     if( n>0 ){
-      *pTokenType = TOKEN_MARKUP;
+      if(n == 3 && z[1] == 'x')
+        *pTokenType = TOKEN_SWHTML;
+      else
+        *pTokenType = TOKEN_MARKUP;
       return n;
-    }else{
+    } else {
       *pTokenType = TOKEN_CHARACTER;
       return 1;
     }
   }
+  
   if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){
     *pTokenType = TOKEN_CHARACTER;
     return 1;
   }
   if( (p->state & ALLOW_WIKI)!=0 ){
@@ -701,10 +706,14 @@
 static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
   int n;
   if( z[0]=='[' && (n = linkLength(z))>0 ){
     *pTokenType = TOKEN_LINK;
     return n;
+  }
+  if( z[0]=='<' && z[1]=='x' && z[2] == '>') {
+    *pTokenType = TOKEN_SWHTML;
+    return 3;
   }
   *pTokenType = TOKEN_RAW;
   return 1 + textLength(z+1, p->state);
 }
 
@@ -821,10 +830,12 @@
   if( p->endTag ){
     blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
   }else{
     blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
     for(i=0; i<p->nAttr; i++){
+	  if(p->aAttr[i].iACode==ATTR_HREF && 0 == fossil_strnicmp(p->aAttr[i].zValue, "JavaScript:", sizeof("JavaScript:") - 1))
+		  continue;
       blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
       if( p->aAttr[i].zValue ){
         const char *zVal = p->aAttr[i].zValue;
         if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
           blob_appendf(pOut, "=\"%s%s\"", g.zTop, zVal);
@@ -926,11 +937,11 @@
 static void popStack(Renderer *p){
   if( p->nStack ){
     int iCode;
     p->nStack--;
     iCode = p->aStack[p->nStack].iCode;
-    if( iCode!=MARKUP_DIV && p->pOut ){
+    if( /*iCode!=MARKUP_DIV &&*/ p->pOut ){
       blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
     }
   }
 }
 
@@ -1273,23 +1284,28 @@
   int n;
   int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
   int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
   int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
   char *zOrig = z;
+  int forceUseHtml = 0;
 
   /* Make sure the attribute constants and names still align
   ** following changes in the attribute list. */
   assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
 
   while( z[0] ){
-    if( wikiHtmlOnly ){
+    if( wikiHtmlOnly || forceUseHtml){
       n = nextRawToken(z, p, &tokenType);
     }else{
       n = nextWikiToken(z, p, &tokenType);
     }
     p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
     switch( tokenType ){
+	    case TOKEN_SWHTML: {
+		    forceUseHtml = !forceUseHtml;
+        break;
+	    }
       case TOKEN_PARAGRAPH: {
         if( inlineOnly ){
           /* blob_append(p->pOut, " &para; ", -1); */
           blob_append(p->pOut, " &nbsp;&nbsp; ", -1);
         }else{
@@ -1521,18 +1537,22 @@
           /* Do nothing */
         }else
 
         /* Generate end-tags */
         if( markup.endTag ){
-          popStackToTag(p, markup.iCode);
+          if(markup.iType==MUTYPE_FONT)
+            renderMarkup(p->pOut, &markup);
+          else
+            popStackToTag(p, markup.iCode);
         }else
 
         /* Push <div> markup onto the stack together with the id=ID attribute.
         */
         if( markup.iCode==MARKUP_DIV ){
           pushStackWithId(p, markup.iCode, markupId(&markup),
                           (p->state & ALLOW_WIKI)!=0);
+          renderMarkup(p->pOut, &markup);
         }else
 
         /* Enter <verbatim> processing.  With verbatim enabled, all other
         ** markup other than the corresponding end-tag with the same ID is
         ** ignored.
@@ -1593,13 +1613,14 @@
             renderMarkup(p->pOut, &markup);
             pushStack(p, markup.iCode);
           }
         }else
         {
-          if( markup.iType==MUTYPE_FONT ){
+          /*if( markup.iType==MUTYPE_FONT ){
             startAutoParagraph(p);
-          }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
+          }else*/
+          if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
             p->wantAutoParagraph = 0;
           }
           if(   markup.iCode==MARKUP_HR
              || markup.iCode==MARKUP_H1
              || markup.iCode==MARKUP_H2
@@ -1907,10 +1928,41 @@
     }
     z += n;
   }
   free(renderer.aStack);
 }
+/*
+** Test text before it add to wiki. Allow use of <x> only for developers or wikieditors.
+*/
+const char* wiki_sanify(const char *pSrc){
+  static Blob tempStore = BLOB_INITIALIZER;
+  int hasReplaces = 0;
+  const char* z = pSrc;
+  Renderer r;
+  
+  blob_reset(&tempStore);
+
+  if(g.perm.Admin || g.perm.NewWiki || g.perm.Write || g.perm.WrWiki || g.perm.Setup)
+    return pSrc;
+  r.state = 1;
+  while(*z)
+  {
+    int tokenType;
+    int n = nextRawToken(z, &r, &tokenType);
+    if(tokenType == TOKEN_SWHTML) {
+      if(!hasReplaces) {
+        blob_append(&tempStore, pSrc, z - pSrc);
+        hasReplaces = 1;
+      }
+      blob_append(&tempStore, "&lt;x&gt;", 9);
+    }
+    else if(hasReplaces)
+      blob_append(&tempStore, z, n);
+    z+=n;
+  }
+  return hasReplaces ? blob_str(&tempStore) : pSrc;
+}
 
 /*
 ** Get the next HTML token.
 **
 ** z points to the start of a token.  Return the number of