OSDN Git Service

#27617 言語名へのリンクを行わない設定を追加し、Wikitravelの設定に言語名を追加,
[wptscs/wpts.git] / Wptscs / Logics / MediaWikiTranslator.cs
index 6576112..9cf9997 100644 (file)
@@ -27,15 +27,28 @@ namespace Honememo.Wptscs.Logics
     using Honememo.Wptscs.Websites;
 
     /// <summary>
-    /// Wikipedia用の翻訳支援処理実装クラスです。
+    /// MediaWiki用の翻訳支援処理実装クラスです。
     /// </summary>
     public class MediaWikiTranslator : Translator
     {
+        #region private変数
+
+        /// <summary>
+        /// <see cref="Translator.ItemTable"/>用ロックオブジェクト。
+        /// </summary>
+        private LockObject itemTableLock = new LockObject();
+
+        #endregion
+
         #region コンストラクタ
 
         /// <summary>
-        /// インスタンスを生成する
+        /// MediaWikiでの翻訳支援処理を行うトランスレータを作成
         /// </summary>
+        /// <remarks>
+        /// 別途プロパティに必要なパラメータを設定する必要あり。
+        /// 通常は<see cref="Translator.Create"/>にて設定ファイルから作成する。
+        /// </remarks>
         public MediaWikiTranslator()
         {
             // このクラス用のロガーと、デフォルトの確認処理としてメッセージダイアログ版を設定
@@ -121,21 +134,23 @@ namespace Honememo.Wptscs.Logics
 
             // 対象記事に言語間リンクが存在する場合、処理を継続するか確認
             // ※ 言語間リンク取得中は、処理状態を解析中に変更
-            string interWiki = null;
-            this.ChangeStatusInExecuting(
-                () => interWiki = article.GetInterlanguage(this.To.Language.Code),
-                Resources.StatusParsing);
-            if (!String.IsNullOrEmpty(interWiki))
+            MediaWikiLink interlanguage;
+            using (var sm = this.StatusManager.Switch(Resources.StatusParsing))
+            {
+                interlanguage = article.GetInterlanguage(this.To.Language.Code);
+            }
+
+            if (interlanguage != null)
             {
                 // 確認処理の最中は処理時間をカウントしない(ダイアログ等を想定するため)
                 this.Stopwatch.Stop();
-                if (this.IsContinueAtInterwikiExisted != null && !this.IsContinueAtInterwikiExisted(interWiki))
+                if (this.IsContinueAtInterwikiExisted != null && !this.IsContinueAtInterwikiExisted(interlanguage.Title))
                 {
                     throw new ApplicationException("user canceled");
                 }
 
                 this.Stopwatch.Start();
-                this.Logger.AddResponse(Resources.LogMessageTargetArticleHadInterWiki, interWiki);
+                this.Logger.AddResponse(Resources.LogMessageTargetArticleHadInterWiki, interlanguage.Title);
             }
 
             // 冒頭部を作成
@@ -143,10 +158,17 @@ namespace Honememo.Wptscs.Logics
 
             // 言語間リンク・定型句の変換、実行中は処理状態を解析中に設定
             this.Logger.AddSeparator();
-            this.Logger.AddResponse(Resources.LogMessageStartParseAndReplace, interWiki);
-            this.ChangeStatusInExecuting(
-                () => this.Text += this.ReplaceElement(new MediaWikiParser(this.From).Parse(article.Text), article.Title).ToString(),
-                Resources.StatusParsing);
+            this.Logger.AddResponse(Resources.LogMessageStartParseAndReplace);
+            using (var sm = this.StatusManager.Switch(Resources.StatusParsing))
+            {
+                IElement element;
+                using (MediaWikiParser parser = new MediaWikiParser(this.From))
+                {
+                    element = parser.Parse(article.Text);
+                }
+
+                this.Text += this.ReplaceElement(element, article).ToString();
+            }
 
             // 記事の末尾に新しい言語間リンクと、コメントを追記
             this.Text += this.CreateEnding(article);
@@ -200,7 +222,7 @@ namespace Honememo.Wptscs.Logics
         protected virtual string CreateOpening(string title)
         {
             string langPart = String.Empty;
-            MediaWikiLink langLink = this.GetLanguageLink(this.From, this.To.Language.Code);
+            IElement langLink = this.GetLanguageLink();
             if (langLink != null)
             {
                 langPart = langLink.ToString() + ": ";
@@ -244,9 +266,9 @@ namespace Honememo.Wptscs.Logics
         /// 渡されたページ要素の変換を行う。
         /// </summary>
         /// <param name="element">ページ要素。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換後のページ要素。</returns>
-        protected virtual IElement ReplaceElement(IElement element, string parent)
+        protected virtual IElement ReplaceElement(IElement element, MediaWikiPage parent)
         {
             // ユーザーからの中止要求をチェック
             this.ThrowExceptionIfCanceled();
@@ -286,20 +308,37 @@ namespace Honememo.Wptscs.Logics
         /// 内部リンクを解析し、変換先言語の記事へのリンクに変換する。
         /// </summary>
         /// <param name="link">変換元リンク。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換済みリンク。</returns>
-        protected virtual IElement ReplaceLink(MediaWikiLink link, string parent)
+        protected virtual IElement ReplaceLink(MediaWikiLink link, MediaWikiPage parent)
         {
             // 記事名が存在しないor自記事内の別セクションへのリンクの場合、記事名絡みの処理を飛ばす
-            if (!this.IsSectionLink(link, parent))
+            if (!this.IsSectionLink(link, parent.Title))
             {
                 // 記事名の種類に応じて処理を実施
                 MediaWikiPage article = new MediaWikiPage(this.From, link.Title);
 
-                if (link.IsSubpage)
+                bool child = false;
+                if (link.IsSubpage())
                 {
-                    // サブページの場合、記事名を補完
-                    link.Title = parent + link.Title;
+                    // サブページ(子)の場合だけ後で記事名を復元するので記録
+                    child = link.Title.StartsWith("/");
+                    
+                    // ページ名を完全な形に補完
+                    string title = parent.Normalize(link);
+                    if (parent.Title.StartsWith(title))
+                    {
+                        // サブページ(親)の場合、変換してもしょうがないのでセクションだけチェックして終了
+                        if (!String.IsNullOrEmpty(link.Section))
+                        {
+                            link.Section = this.ReplaceLinkSection(link.Section);
+                            link.ParsedString = null;
+                        }
+
+                        return link;
+                    }
+
+                    link.Title = title;
                 }
                 else if (!String.IsNullOrEmpty(link.Interwiki))
                 {
@@ -318,14 +357,6 @@ namespace Honememo.Wptscs.Logics
                     // カテゴリ用の変換を実施
                     return this.ReplaceLinkCategory(link);
                 }
-                else if (StringUtils.DefaultString(link.Title).StartsWith("../"))
-                {
-                    // ..形式のサブページが処理できない既知の不具合への対応、警告メッセージを出す
-                    // ※ 2012年2月現在、..形式のサブページはIsSubpageも立たない
-                    this.Logger.AddSource(link);
-                    this.Logger.AddResponse(Resources.LogMessageErrorPageName, link.Title);
-                    return link;
-                }
 
                 // 専用処理の無い内部リンクの場合、言語間リンクによる置き換えを行う
                 string interWiki = this.GetInterlanguage(link);
@@ -345,9 +376,10 @@ namespace Honememo.Wptscs.Logics
                     link.Title = this.From.Language.Code + ':' + link.Title;
                     link.IsColon = true;
                 }
-                else if (link.IsSubpage)
+                else if (child)
                 {
-                    // 言語間リンクが存在してサブページの場合、親ページ部分を消す
+                    // 言語間リンクが存在してサブページ(子)の場合、親ページ部分を消す
+                    // TODO: 兄弟や叔父のパターンも対処したい(ややこしいので現状未対応)
                     link.Title = StringUtils.Substring(interWiki, interWiki.IndexOf('/'));
                 }
                 else
@@ -380,9 +412,9 @@ namespace Honememo.Wptscs.Logics
         /// テンプレートを解析し、変換先言語の記事へのテンプレートに変換する。
         /// </summary>
         /// <param name="template">変換元テンプレート。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換済みテンプレート。</returns>
-        protected virtual IElement ReplaceTemplate(MediaWikiTemplate template, string parent)
+        protected virtual IElement ReplaceTemplate(MediaWikiTemplate template, MediaWikiPage parent)
         {
             // システム変数({{PAGENAME}}とか)の場合は対象外
             if (this.From.IsMagicWord(template.Title))
@@ -431,9 +463,9 @@ namespace Honememo.Wptscs.Logics
         /// 指定された見出しに対して、対訳表による変換を行う。
         /// </summary>
         /// <param name="heading">見出し。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換後の見出し。</returns>
-        protected virtual IElement ReplaceHeading(MediaWikiHeading heading, string parent)
+        protected virtual IElement ReplaceHeading(MediaWikiHeading heading, MediaWikiPage parent)
         {
             // 変換元ログ出力
             this.Logger.AddSource(heading);
@@ -464,9 +496,9 @@ namespace Honememo.Wptscs.Logics
         /// 変数要素を再帰的に解析し、変換先言語の記事への要素に変換する。
         /// </summary>
         /// <param name="variable">変換元変数要素。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換済み変数要素。</returns>
-        protected virtual IElement ReplaceVariable(MediaWikiVariable variable, string parent)
+        protected virtual IElement ReplaceVariable(MediaWikiVariable variable, MediaWikiPage parent)
         {
             // 変数、これ自体は処理しないが、再帰的に探索
             string old = variable.Value.ToString();
@@ -484,9 +516,9 @@ namespace Honememo.Wptscs.Logics
         /// 要素を再帰的に解析し、変換先言語の記事への要素に変換する。
         /// </summary>
         /// <param name="listElement">変換元要素。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換済み要素。</returns>
-        protected virtual IElement ReplaceListElement(ListElement listElement, string parent)
+        protected virtual IElement ReplaceListElement(ListElement listElement, MediaWikiPage parent)
         {
             // 値を格納する要素、これ自体は処理しないが、再帰的に探索
             for (int i = 0; i < listElement.Count; i++)
@@ -521,52 +553,15 @@ namespace Honememo.Wptscs.Logics
             }
 
             // 以下マルチスレッドで使われることも想定して対訳表へのアクセス時はロック
-            lock (this.ItemTable)
-            {
-                // 対訳表へのキーとしてはHTMLデコードした記事名を使用する
-                return this.ItemTable.ContainsKey(WebUtility.HtmlDecode(title));
-            }
-        }
-
-        /// <summary>
-        /// 対訳表から指定された記事名の情報を取得する。
-        /// </summary>
-        /// <param name="title">記事名。</param>
-        /// <param name="item">翻訳先情報。</param>
-        /// <returns>指定した記事の情報が登録されている場合<c>true</c>。</returns>
-        /// <remarks>複数スレッドからのアクセスに対応する。</remarks>
-        protected bool TryGetValueAtItemTable(string title, out TranslationDictionary.Item item)
-        {
-            // 以下マルチスレッドで使われることも想定して対訳表へのアクセス時はロック
-            // ※ 同時アクセスを防いでいるだけで、更新処理とは同期していない。
-            //    現状では同じページを同時に解析してしまう可能性があり、効率が良いソースではない。
-            //    また、効率を目指すならItemTableではなく記事名ごとに一意なオブジェクトをロックすべき
-            lock (this.ItemTable)
-            {
-                // 対訳表へのキーとしてはHTMLデコードした記事名を使用する
-                return this.ItemTable.TryGetValue(WebUtility.HtmlDecode(title), out item);
-            }
-        }
-
-        /// <summary>
-        /// 対訳表に指定された記事名の情報を登録する。
-        /// </summary>
-        /// <param name="title">記事名。</param>
-        /// <param name="item">翻訳先情報。</param>
-        /// <remarks>複数スレッドからのアクセスに対応する。</remarks>
-        protected void PutValueAtItemTable(string title, TranslationDictionary.Item item)
-        {
-            // 以下マルチスレッドで使われることも想定して対訳表へのアクセス時はロック
-            // ※ 同時アクセスを防いでいるだけで、読込処理とは同期していない。
-            //    現状では同じページを同時に解析してしまう可能性があり、効率が良いソースではない。
-            //    また、効率を目指すならItemTableではなく記事名ごとに一意なオブジェクトをロックすべき
-            lock (this.ItemTable)
+            // ※ 対訳表へのアクセス時は記事名をデコードしておく
+            string decodedTitle = WebUtility.HtmlDecode(title);
+            lock (this.itemTableLock.GetObject(decodedTitle.ToLower()))
             {
                 // 対訳表へのキーとしてはHTMLデコードした記事名を使用する
-                this.ItemTable[WebUtility.HtmlDecode(title)] = item;
+                return this.ItemTable.ContainsKey(decodedTitle);
             }
         }
-
+        
         /// <summary>
         /// 指定されたコードでの見出しに相当する、別の言語での見出しを取得。
         /// </summary>
@@ -605,38 +600,45 @@ namespace Honememo.Wptscs.Logics
                 return this.GetInterlanguageWithCreateCache(title, out item);
             }
 
-            // 対訳表を使用して言語間リンクを探索
-            if (this.TryGetValueAtItemTable(title, out item))
+            // 対訳表を使用して言語間リンクを探索。
+            // 以下マルチスレッドで使われることも想定して対訳表へのアクセス時はロック(記事名単位)。
+            // また、対訳表へのアクセス時は記事名をデコードしておく。
+            string decodedTitle = WebUtility.HtmlDecode(title);
+            lock (this.itemTableLock.GetObject(decodedTitle.ToLower()))
             {
-                // 存在する場合はその値を使用
-                if (!String.IsNullOrWhiteSpace(item.Alias))
+                if (this.ItemTable.TryGetValue(decodedTitle, out item))
                 {
-                    // リダイレクトがあれば、そのメッセージも表示
-                    this.Logger.AddAlias(new MediaWikiLink(item.Alias));
-                }
+                    // 存在する場合はその値を使用
+                    if (!String.IsNullOrWhiteSpace(item.Alias))
+                    {
+                        // リダイレクトがあれば、そのメッセージも表示
+                        this.Logger.AddAlias(new MediaWikiLink(item.Alias));
+                    }
 
-                if (!String.IsNullOrEmpty(item.Word))
-                {
-                    this.Logger.AddDestination(new MediaWikiLink(item.Word), true);
-                    return item.Word;
+                    if (!String.IsNullOrEmpty(item.Word))
+                    {
+                        this.Logger.AddDestination(new MediaWikiLink(item.Word), true);
+                        return item.Word;
+                    }
+                    else
+                    {
+                        this.Logger.AddDestination(new TextElement(Resources.LogMessageInterWikiNotFound), true);
+                        return String.Empty;
+                    }
                 }
-                else
+
+                // 対訳表に存在しない場合は、普通に取得し表に記録
+                // ※ こちらは内部でデコードしているためデコードした記事名を渡してはならない
+                string interlanguage = this.GetInterlanguageWithCreateCache(title, out item);
+                if (interlanguage != null)
                 {
-                    this.Logger.AddDestination(new TextElement(Resources.LogMessageInterWikiNotFound), true);
-                    return String.Empty;
+                    // ページ自体が存在しない場合を除き、結果を対訳表に登録
+                    // ※ キャッシュとしては登録すべきかもしれないが、一応"対訳表"であるので
+                    this.ItemTable[decodedTitle] = item;
                 }
-            }
 
-            // 対訳表に存在しない場合は、普通に取得し表に記録
-            string interWiki = this.GetInterlanguageWithCreateCache(title, out item);
-            if (interWiki != null)
-            {
-                // ページ自体が存在しない場合を除き、結果を対訳表に登録
-                // ※ キャッシュとしては登録すべきかもしれないが、一応"対訳表"であるので
-                this.PutValueAtItemTable(title, item);
+                return interlanguage;
             }
-
-            return interWiki;
         }
 
         /// <summary>
@@ -659,23 +661,27 @@ namespace Honememo.Wptscs.Logics
                 page = this.GetDestinationPage(page.Redirect.Title);
             }
 
+            if (page == null)
+            {
+                // ページ自体が存在しない場合はnull
+                return null;
+            }
+
             // 記事があればその言語間リンクを取得
-            string interWiki = null;
-            if (page != null)
+            MediaWikiLink interlanguage = page.GetInterlanguage(this.To.Language.Code);
+            if (interlanguage != null)
             {
-                interWiki = page.GetInterlanguage(this.To.Language.Code);
-                item.Word = interWiki;
-                if (!String.IsNullOrEmpty(interWiki))
-                {
-                    this.Logger.AddDestination(new MediaWikiLink(interWiki));
-                }
-                else
-                {
-                    this.Logger.AddDestination(new TextElement(Resources.LogMessageInterWikiNotFound));
-                }
+                item.Word = interlanguage.Title;
+                this.Logger.AddDestination(interlanguage);
+            }
+            else
+            {
+                // 見つからない場合は空
+                item.Word = String.Empty;
+                this.Logger.AddDestination(new TextElement(Resources.LogMessageInterWikiNotFound));
             }
 
-            return interWiki;
+            return item.Word;
         }
 
         /// <summary>
@@ -790,9 +796,9 @@ namespace Honememo.Wptscs.Logics
         /// ファイル指定の内部リンクを解析し、変換先言語で参照可能なファイルへのリンクに変換する。
         /// </summary>
         /// <param name="link">変換元リンク。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換済みリンク。</returns>
-        private IElement ReplaceLinkFile(MediaWikiLink link, string parent)
+        private IElement ReplaceLinkFile(MediaWikiLink link, MediaWikiPage parent)
         {
             // 名前空間を翻訳先言語の書式に変換、またパラメータ部を再帰的に処理
             link.Title = this.ReplaceLinkNamespace(link.Title, this.To.FileNamespace);
@@ -854,9 +860,9 @@ namespace Honememo.Wptscs.Logics
         /// 渡された要素リストに対して<see cref="ReplaceElement"/>による変換を行う。
         /// </summary>
         /// <param name="elements">変換元要素リスト。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>変換済み要素リスト。</returns>
-        private IList<IElement> ReplaceElements(IList<IElement> elements, string parent)
+        private IList<IElement> ReplaceElements(IList<IElement> elements, MediaWikiPage parent)
         {
             if (elements == null)
             {
@@ -876,41 +882,27 @@ namespace Honememo.Wptscs.Logics
         /// テンプレート名に必要に応じて名前空間を補完する。
         /// </summary>
         /// <param name="template">テンプレート。</param>
-        /// <param name="parent">ã\82µã\83\96ã\83\9aã\83¼ã\82¸ç\94¨ã\81®è¦ªè¨\98äº\8bã\82¿ã\82¤ã\83\88ã\83«。</param>
+        /// <param name="parent">ã\83\9aã\83¼ã\82¸è¦\81ç´ ã\82\92å\8f\96å¾\97ã\81\97ã\81\9få¤\89æ\8f\9bå\85\83è¨\98äº\8b。</param>
         /// <returns>補完済みのテンプレート名。</returns>
-        private string FillTemplateName(MediaWikiTemplate template, string parent)
+        private string FillTemplateName(MediaWikiTemplate template, MediaWikiPage parent)
         {
-            if (template.IsColon || !new MediaWikiPage(this.From, template.Title).IsMain())
-            {
-                // 標準名前空間が指定されている(先頭にコロンが無い)
-                // または何かしらの名前空間が指定されている場合、補完不要
-                return template.Title;
-            }
-            else if (template.IsSubpage)
+            // プレフィックスが付いた記事名を作成
+            string filledTitle = parent.Normalize(template);
+            if (filledTitle == template.Title || template.IsSubpage())
             {
-                // サブページの場合、親記事名での補完のみ
-                return parent + template.Title;
-            }
-
-            // 補完する必要がある場合、名前空間のプレフィックス(Template等)を取得
-            string prefix = this.GetTemplatePrefix();
-            if (String.IsNullOrEmpty(prefix))
-            {
-                // 名前空間の設定が存在しない場合、何も出来ないため終了
-                return template.Title;
+                // 補完が不要な場合、またはサブページだった場合、ここで終了
+                return filledTitle;
             }
 
-            // 頭にプレフィックスを付けた記事名で実在するかをチェック
-            string filledTitle = prefix + ":" + template.Title;
-
-            // 既に対訳表にプレフィックス付きの記事名が確認されているか?
+            // プレフィックスが付いた記事名が実際に存在するかを確認
+            // ※ 不要かもしれないが、マジックワードの漏れ等の誤検出を減らしたいので
             if (this.ContainsAtItemTable(filledTitle))
             {
-                // 記事が存在する場合、プレフィックスをつけた名前を使用
+                // 対訳表に記事名が確認されている場合、既知の名前として確定
                 return filledTitle;
             }
 
-            // 未確認の場合、実際に頭にプレフィックスを付けた記事名でアクセスし、存在するかをチェック
+            // 実際に頭にプレフィックスを付けた記事名でアクセスし、存在するかをチェック
             // TODO: GetInterWikiの方とあわせ、テンプレートでは2度GetPageが呼ばれている。可能であれば共通化する
             MediaWikiPage page = null;
             try
@@ -930,30 +922,16 @@ namespace Honememo.Wptscs.Logics
                 if (!Settings.Default.IgnoreError)
                 {
                     // エラーを無視しない場合、ここで翻訳支援処理を中断する
+                    this.Logger.AddError(e);
                     throw new ApplicationException(e.Message, e);
                 }
 
                 // 続行する場合は、とりあえずプレフィックスをつけた名前で処理
-                this.Logger.AddMessage(Resources.LogMessageTemplateNameUnidentified, template.Title, prefix, e.Message);
+                this.Logger.AddResponse(Resources.LogMessageTemplateNameUnidentified, template.Title, filledTitle, e.Message);
                 return filledTitle;
             }
         }
 
-        /// <summary>
-        /// テンプレート名前空間のプレフィックスを取得。
-        /// </summary>
-        /// <returns>プレフィックス。取得できない場合<c>null</c></returns>
-        private string GetTemplatePrefix()
-        {
-            ISet<string> prefixes = this.From.Namespaces[this.From.TemplateNamespace];
-            if (prefixes != null)
-            {
-                return prefixes.FirstOrDefault();
-            }
-
-            return null;
-        }
-
         #endregion
 
         #region その他内部処理用メソッド
@@ -963,6 +941,12 @@ namespace Honememo.Wptscs.Logics
         /// </summary>
         /// <param name="title">翻訳支援対象の記事名。</param>
         /// <returns>取得したページ。取得失敗時は<c>null</c>。</returns>
+        /// <remarks>
+        /// ここで取得した記事のURIを、以後の翻訳支援処理で使用するRefererとして登録
+        /// (ここで処理しているのは、リダイレクトの場合のリダイレクト先への
+        /// アクセス時にもRefererを入れたかったから。
+        /// リダイレクトの場合は、最終的には転送先ページのURIとなる)。
+        /// </remarks>
         private MediaWikiPage GetTargetPage(string title)
         {
             // 指定された記事をWikipediaから取得、リダイレクトの場合その先まで探索
@@ -979,9 +963,14 @@ namespace Honememo.Wptscs.Logics
                     this.Logger.AddResponse(Resources.LogMessageTargetArticleNotFound);
                     break;
                 }
-                else if (!page.IsRedirect())
+
+                // 取得した記事のURIを以後のアクセスで用いるRefererとして登録
+                this.From.WebProxy.Referer = page.Uri.ToString();
+                this.To.WebProxy.Referer = page.Uri.ToString();
+
+                if (!page.IsRedirect())
                 {
-                    // ã\83ªã\83\80ã\82¤ã\83¬ã\82¯ã\83\88以å¤\96ã\82\82ã\81\93ã\81\93で終了
+                    // ã\83ªã\83\80ã\82¤ã\83¬ã\82¯ã\83\88以å¤\96ã\81ªã\82\89ã\81\93ã\82\8cで終了
                     break;
                 }
 
@@ -996,24 +985,48 @@ namespace Honememo.Wptscs.Logics
         /// <summary>
         /// 指定した言語での言語名称を [[言語名称|略称]]の内部リンクで取得。
         /// </summary>
-        /// <param name="site">サイト。</param>
-        /// <param name="code">言語のコード。</param>
-        /// <returns>[[言語名称|略称]]の内部リンク。登録されていない場合<c>null</c>。</returns>
-        private MediaWikiLink GetLanguageLink(Website site, string code)
+        /// <returns>
+        /// [[言語名称|略称]]の内部リンク。登録されていない場合<c>null</c>。
+        /// サーバーにそうした記事が存在しない場合、リンクではなく言語名称or略称の文字列を返す。
+        /// </returns>
+        private IElement GetLanguageLink()
         {
-            if (!site.Language.Names.ContainsKey(code))
+            // 言語情報を取得
+            Language.LanguageName name;
+            if (!this.From.Language.Names.TryGetValue(this.To.Language.Code, out name))
             {
                 return null;
             }
 
-            Language.LanguageName name = site.Language.Names[code];
-            MediaWikiLink link = new MediaWikiLink(name.Name);
+            // 略称を取得
+            IElement shortName = null;
             if (!String.IsNullOrEmpty(name.ShortName))
             {
-                link.PipeTexts.Add(new TextElement(name.ShortName));
+                shortName = new TextElement(name.ShortName);
             }
 
-            return link;
+            if (this.To.HasLanguagePage)
+            {
+                // サーバーにこの言語の記事が存在することが期待される場合、
+                // 内部リンクとして返す
+                MediaWikiLink link = new MediaWikiLink(name.Name);
+                if (shortName != null)
+                {
+                    link.PipeTexts.Add(shortName);
+                }
+
+                return link;
+            }
+            else if (shortName != null)
+            {
+                // 存在しない場合、まずあれば略称を返す
+                return shortName;
+            }
+            else
+            {
+                // 無ければ言語名を返す
+                return new TextElement(name.Name);
+            }
         }
 
         /// <summary>