1 // ================================================================================================
3 // Wikipedia用の翻訳支援処理実装クラスソース</summary>
5 // <copyright file="MediaWikiTranslator.cs" company="honeplusのメモ帳">
6 // Copyright (C) 2010 Honeplus. All rights reserved.</copyright>
9 // ================================================================================================
11 namespace Honememo.Wptscs.Logics
14 using System.Collections.Generic;
18 using System.Windows.Forms;
19 using Honememo.Utilities;
20 using Honememo.Wptscs.Models;
21 using Honememo.Wptscs.Properties;
24 /// Wikipedia用の翻訳支援処理実装クラスです。
26 public class MediaWikiTranslator : Translator
33 public new MediaWiki From
37 return base.From as MediaWiki;
49 public new MediaWiki To
53 return base.To as MediaWiki;
69 /// <param name="comment">解析したコメント。</param>
70 /// <param name="text">解析するテキスト。</param>
71 /// <param name="index">解析開始インデックス。</param>
72 /// <returns>コメント区間の場合、終了位置のインデックスを返す。それ以外は-1。</returns>
73 public static int ChkComment(out string comment, string text, int index)
76 if (String.IsNullOrEmpty(text))
78 comment = String.Empty;
83 if (!LazyXmlParser.TryParseComment(text.Substring(index), out comment))
85 comment = String.Empty;
89 return index + comment.Length - 1;
95 /// <param name="nowiki">解析したnowikiブロック。</param>
96 /// <param name="text">解析するテキスト。</param>
97 /// <param name="index">解析開始インデックス。</param>
98 /// <returns>nowiki区間の場合、終了位置のインデックスを返す。それ以外は-1。</returns>
99 public static int ChkNowiki(out string nowiki, string text, int index)
102 if (String.IsNullOrEmpty(text))
104 nowiki = String.Empty;
109 if (!MediaWikiPage.TryParseNowiki(text.Substring(index), out nowiki))
111 nowiki = String.Empty;
115 return index + nowiki.Length - 1;
124 /// ※継承クラスでは、この関数に処理を実装すること
126 /// <param name="name">記事名。</param>
127 /// <returns><c>true</c> 処理成功。</returns>
128 protected override bool RunBody(string name)
130 System.Diagnostics.Debug.WriteLine("\nMediaWikiTranslator.runBody > " + name);
133 MediaWikiPage article = this.ChkTargetArticle(name);
139 // 対象記事に言語間リンクが存在する場合、処理を継続するか確認
140 string interWiki = article.GetInterWiki(this.To.Language.Code);
141 if (interWiki != String.Empty)
144 String.Format(Resources.QuestionMessage_ArticleExist, interWiki),
145 Resources.QuestionTitle,
146 MessageBoxButtons.YesNo,
147 MessageBoxIcon.Question)
148 == System.Windows.Forms.DialogResult.No)
150 this.LogLine(ENTER + String.Format(Resources.QuestionMessage_ArticleExist, interWiki));
155 this.LogLine(Resources.RightArrow + " " + String.Format(Resources.LogMessage_ArticleExistInterWiki, interWiki));
160 this.Text += "'''xxx'''";
161 string bracket = this.To.Language.Bracket;
162 if (bracket.Contains("{0}"))
164 string originalName = String.Empty;
165 string langTitle = this.GetFullName(this.From, this.To.Language.Code);
166 if (langTitle != String.Empty)
168 originalName = "[[" + langTitle + "]]: ";
171 this.Text += String.Format(bracket, originalName + "'''" + name + "'''");
177 this.LogLine(ENTER + Resources.RightArrow + " " + String.Format(Resources.LogMessage_CheckAndReplaceStart, interWiki));
178 this.Text += this.ReplaceText(article.Text, article.Title);
181 if (CancellationPending)
186 // 新しい言語間リンクと、コメントを追記
187 this.Text += "\n\n[[" + this.From.Language.Code + ":" + name + "]]\n";
188 this.Text += String.Format(
189 Resources.ArticleFooter,
190 FormUtils.ApplicationName(),
191 this.From.Language.Code,
193 article.Timestamp.HasValue ? article.Timestamp.Value.ToString("U") : String.Empty) + "\n";
195 // ダウンロードされるテキストがLFなので、ここで全てCRLFに変換
196 // ※ダウンロード時にCRLFにするような仕組みが見つかれば、そちらを使う
197 // その場合、上のように\nをべたに吐いている部分を修正する
198 this.Text = this.Text.Replace("\n", ENTER);
200 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.runBody > Success!");
206 #region 他のクラスの処理をこのクラスにあわせて拡張したメソッド
209 /// ログメッセージを出力しつつページを取得。
211 /// <param name="title">ページタイトル。</param>
212 /// <param name="notFoundMsg">取得できない場合に出力するメッセージ。</param>
213 /// <returns>取得したページ。ページが存在しない場合は <c>null</c> を返す。</returns>
214 /// <remarks>通信エラーなど例外が発生した場合は、別途エラーログを出力する。</remarks>
215 protected new MediaWikiPage GetPage(string title, string notFoundMsg)
217 // 親クラスのメソッドを戻り値の型だけ変更
218 return base.GetPage(title, notFoundMsg) as MediaWikiPage;
228 /// <param name="title">ページ名。</param>
229 /// <returns>取得したページ。取得失敗時は<c>null</c>。</returns>
230 protected MediaWikiPage ChkTargetArticle(string title)
232 // 指定された記事の生データをWikipediaから取得
233 this.LogLine(String.Format(Resources.LogMessage_GetArticle, this.From.Location, title));
234 MediaWikiPage page = this.GetPage(title, Resources.RightArrow + " " + Resources.LogMessage_ArticleNothing);
236 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
237 if (page != null && page.IsRedirect())
239 this.LogLine(Resources.RightArrow + " " + Resources.LogMessage_Redirect + " [[" + page.Redirect.Title + "]]");
240 page = this.GetPage(page.Redirect.Title, Resources.RightArrow + " " + Resources.LogMessage_ArticleNothing);
247 /// ログメッセージを出力しつつ、指定された記事の指定された言語コードへの言語間リンクを返す。
249 /// <param name="title">記事名。</param>
250 /// <param name="code">言語コード。</param>
251 /// <returns>言語間リンク先の記事名。見つからない場合は空。ページ自体が存在しない場合は<c>null</c>。</returns>
252 protected string GetInterWiki(string title, string code)
254 MediaWikiPage page = this.GetPage(title, Resources.LogMessage_LinkArticleNothing);
256 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
257 if (page != null && page.IsRedirect())
259 this.Log += Resources.LogMessage_Redirect + " [[" + page.Redirect.Title + "]] " + Resources.RightArrow + " ";
260 page = this.GetPage(page.Redirect.Title, Resources.LogMessage_LinkArticleNothing);
264 string interWiki = null;
267 interWiki = page.GetInterWiki(this.To.Language.Code);
268 if (!String.IsNullOrEmpty(interWiki))
270 Log += "[[" + interWiki + "]]";
274 Log += Resources.LogMessage_InterWikiNothing;
282 /// ログメッセージを出力しつつ、指定された記事の指定された言語コードへの言語間リンクを返す。
284 /// <param name="title">記事名。</param>
285 /// <param name="code">言語コード。</param>
286 /// <returns>言語間リンク先の記事名。見つからない場合は空。ページ自体が存在しない場合は<c>null</c>。</returns>
287 /// <remarks>対訳表が指定されている場合、その内容を使用する。また取得結果を対訳表に追加する。</remarks>
288 protected string GetInterWikiUseTable(string title, string code)
290 if (this.ItemTable == null)
292 // 対訳表が指定されていない場合は、普通に記事を取得
293 return this.GetInterWiki(title, code);
296 string interWiki = null;
297 lock (this.ItemTable)
299 TranslationDictionary.Item item;
300 if (this.ItemTable.TryGetValue(title, out item))
303 // リダイレクトがあれば、そのメッセージも表示
304 if (!String.IsNullOrWhiteSpace(item.Alias))
306 this.Log += Resources.LogMessage_Redirect + " [[" + item.Alias + "]] " + Resources.RightArrow + " ";
309 if (!String.IsNullOrEmpty(item.Word))
311 interWiki = item.Word;
312 Log += "[[" + interWiki + "]]";
316 interWiki = String.Empty;
317 Log += Resources.LogMessage_InterWikiNothing;
320 Log += Resources.LogMessageTranslation;
324 // 対訳表に存在しない場合は、普通に取得し表に記録
325 // ※ nullも存在しないことの記録として格納
326 item = new TranslationDictionary.Item { Timestamp = DateTime.UtcNow };
327 MediaWikiPage page = this.GetPage(title, Resources.LogMessage_LinkArticleNothing);
329 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
330 if (page != null && page.IsRedirect())
332 item.Alias = page.Redirect.Title;
333 this.Log += Resources.LogMessage_Redirect + " [[" + page.Redirect.Title + "]] " + Resources.RightArrow + " ";
334 page = this.GetPage(page.Redirect.Title, Resources.LogMessage_LinkArticleNothing);
340 interWiki = page.GetInterWiki(this.To.Language.Code);
341 if (!String.IsNullOrEmpty(interWiki))
343 Log += "[[" + interWiki + "]]";
347 Log += Resources.LogMessage_InterWikiNothing;
350 item.Word = interWiki;
351 this.ItemTable[title] = item;
359 /// 指定された記事を取得し、言語間リンクを確認、返す。
361 /// <param name="title">記事名。</param>
362 /// <param name="template"><c>true</c> テンプレート。</param>
363 /// <returns>言語間リンク先の記事、存在しない場合 <c>null</c>。</returns>
364 protected string GetInterWiki(string title, bool template)
366 // 指定された記事の生データをWikipediaから取得
367 // ※記事自体が存在しない場合、NULLを返す
370 Log += "[[" + title + "]] " + Resources.RightArrow + " ";
374 Log += "{{" + title + "}} " + Resources.RightArrow + " ";
377 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
378 string interWiki = this.GetInterWikiUseTable(title, this.To.Language.Code);
380 // 改行が出力されていない場合(正常時)、改行
381 if (!Log.EndsWith(ENTER))
390 /// 指定された記事を取得し、言語間リンクを確認、返す(テンプレート以外)。
392 /// <param name="name">記事名。</param>
393 /// <returns>言語間リンク先の記事、存在しない場合 <c>null</c>。</returns>
394 protected string GetInterWiki(string name)
396 return this.GetInterWiki(name, false);
400 /// 渡されたテキストを解析し、言語間リンク・見出し等の変換を行う。
402 /// <param name="text">記事テキスト。</param>
403 /// <param name="parent">元記事タイトル。</param>
404 /// <param name="headingEnable">見出しのチェックを行うか?</param>
405 /// <returns>変換後の記事テキスト。</returns>
406 protected string ReplaceText(string text, string parent, bool headingEnable)
408 // 指定された記事の言語間リンク・見出しを探索し、翻訳先言語での名称に変換し、それに置換した文字列を返す
409 StringBuilder b = new StringBuilder();
410 bool enterFlag = true;
411 MediaWikiPage wikiAP = new MediaWikiPage(this.From, "dummy", null);
412 for (int i = 0; i < text.Length; i++)
415 if (CancellationPending == true)
425 // 改行の場合、次のループで見出し行チェックを行う
433 // 行の始めでは、その行が見出しの行かのチェックを行う
437 int index2 = this.ChkTitleLine(out newTitleLine, text, i);
444 b.Append(newTitleLine);
456 int index = MediaWikiTranslator.ChkComment(out comment, text, i);
461 if (comment.Contains("\n") == true)
471 index = MediaWikiTranslator.ChkNowiki(out nowiki, text, i);
479 // 変数({{{1}}}とか)のチェック
482 index = wikiAP.ChkVariable(out variable, out value, text, i);
487 // 変数の | 以降に値が記述されている場合、それに対して再帰的に処理を行う
488 int valueIndex = variable.IndexOf('|');
489 if (valueIndex != -1 && !String.IsNullOrEmpty(value))
491 variable = variable.Substring(0, valueIndex + 1) + this.ReplaceText(value, parent) + "}}}";
498 // 内部リンク・テンプレートのチェック&変換、言語間リンクを取得し出力する
500 index = this.ReplaceLink(out subtext, text, i, parent);
516 /// 渡されたテキストを解析し、言語間リンク・見出し等の変換を行う。
518 /// <param name="text">記事テキスト。</param>
519 /// <param name="parent">元記事タイトル。</param>
520 /// <returns>変換後の記事テキスト。</returns>
521 protected string ReplaceText(string text, string parent)
523 return this.ReplaceText(text, parent, true);
529 /// <param name="link">解析したリンク。</param>
530 /// <param name="text">解析するテキスト。</param>
531 /// <param name="index">解析開始インデックス。</param>
532 /// <param name="parent">元記事タイトル。</param>
533 /// <returns>リンクの場合、終了位置のインデックスを返す。それ以外は-1。</returns>
534 protected int ReplaceLink(out string link, string text, int index, string parent)
539 MediaWikiPage.Link l;
541 // 内部リンク・テンプレートの確認と解析
542 MediaWikiPage wikiAP = new MediaWikiPage(this.From, "dummy", null);
543 lastIndex = wikiAP.ChkLinkText(out l, text, index);
546 // 記事名に変数が使われている場合があるので、そのチェックと展開
547 int subindex = l.Title.IndexOf("{{{");
552 int lastIndex2 = wikiAP.ChkVariable(out variable, out value, l.Title, subindex);
553 if (lastIndex2 != -1 && !String.IsNullOrEmpty(value))
555 // 変数の | 以降に値が記述されている場合、それに置き換える
556 string newArticle = l.Title.Substring(0, subindex) + value;
557 if (lastIndex2 + 1 < l.Title.Length)
559 newArticle += l.Title.Substring(lastIndex2 + 1);
562 l.Title = newArticle;
566 // 値が設定されていない場合、処理してもしょうがないので、除外
567 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceLink > 対象外 : " + l.OriginalText);
572 string newText = null;
575 if (text[index] == '[')
578 newText = this.ReplaceInnerLink(l, parent);
580 else if (text[index] == '{')
584 newText = this.ReplaceTemplate(l, parent);
589 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceLink > プログラムミス : " + l.OriginalText);
609 /// <param name="link">変換元リンク文字列。</param>
610 /// <param name="parent">元記事タイトル。</param>
611 /// <returns>変換済みリンク文字列。</returns>
612 protected string ReplaceInnerLink(MediaWikiPage.Link link, string parent)
615 StringBuilder b = new StringBuilder("[[");
616 string comment = String.Empty;
617 MediaWikiPage.Link l = link;
619 // 記事内を指している場合([[#関連項目]]だけとか)以外
620 if (!String.IsNullOrEmpty(l.Title) &&
621 !(l.Title == parent && String.IsNullOrEmpty(l.Code) && !String.IsNullOrEmpty(l.Section)))
623 // 変換の対象外とするリンクかをチェック
624 MediaWikiPage article = new MediaWikiPage(this.From, l.Title);
629 l.Title = parent + l.Title;
631 else if (!String.IsNullOrEmpty(l.Code))
633 // 言語間リンク・姉妹プロジェクトへのリンクは対象外
634 // 先頭が : でない、翻訳先言語への言語間リンクの場合
635 if (!l.IsColon && l.Code == this.To.Language.Code)
637 // 削除する。正常終了で、置換後文字列なしを返す
638 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceInnerLink > " + l.OriginalText + " を削除");
643 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceInnerLink > 対象外 : " + l.OriginalText);
646 else if (article.IsFile())
648 // 画像も対象外だが、名前空間だけ翻訳先言語の書式に変換
649 return this.ReplaceFileLink(l);
652 // リンクを辿り、対象記事の言語間リンクを取得
653 string interWiki = this.GetInterWiki(l.Title);
655 // 記事自体が存在しない(赤リンク)場合、リンクはそのまま
656 if (interWiki == null)
660 else if (interWiki == String.Empty)
662 // 言語間リンクが存在しない場合、[[:en:xxx]]みたいな形式に置換
664 b.Append(this.From.Language.Code);
670 // 言語間リンクが存在する場合、そちらを指すように置換
674 int index = interWiki.IndexOf('/');
680 b.Append(interWiki.Substring(index));
693 // カテゴリーの場合は、コメントで元の文字列を追加する
694 if (article.IsCategory() && !l.IsColon)
696 comment = "<!-- " + l.OriginalText + " -->";
698 // カテゴリーで[[:en:xxx]]みたいな形式にした場合、| 以降は不要なので削除
699 if (interWiki == String.Empty)
701 l.PipeTexts = new List<string>();
704 else if (l.PipeTexts.Count == 0 && interWiki != null)
706 // 表示名が存在しない場合、元の名前を表示名に設定
707 l.PipeTexts.Add(article.Title);
711 // 見出し([[#関連項目]]とか)を出力
712 if (!String.IsNullOrEmpty(l.Section))
715 string heading = this.GetHeading(l.Section);
717 b.Append(heading != null ? heading : l.Section);
721 foreach (string text in l.PipeTexts)
724 if (!String.IsNullOrEmpty(text))
726 // 画像の場合、| の後に内部リンクやテンプレートが書かれている場合があるが、
727 // 画像は処理対象外でありその中のリンクは個別に再度処理されるため、ここでは特に何もしない
736 if (comment != String.Empty)
741 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceInnerLink > " + l.OriginalText);
748 /// <param name="link">変換元テンプレート文字列。</param>
749 /// <param name="parent">元記事タイトル。</param>
750 /// <returns>変換済みテンプレート文字列。</returns>
751 protected string ReplaceTemplate(MediaWikiPage.Link link, string parent)
754 MediaWikiPage.Link l = link;
757 if (String.IsNullOrEmpty(l.Title))
759 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceTemplate > 対象外 : " + l.OriginalText);
764 if (this.From.IsMagicWord(l.Title))
766 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceTemplate > システム変数 : " + l.OriginalText);
770 // テンプレート名前空間か、普通の記事かを判定
771 if (!l.IsColon && !l.IsSubpage)
773 string prefix = null;
774 IList<string> prefixes = this.From.Namespaces[this.From.TemplateNamespace];
775 if (prefixes != null && prefixes.Count > 0)
777 prefix = prefixes[0];
780 if (!String.IsNullOrEmpty(prefix) && !l.Title.StartsWith(prefix + ":"))
782 // 頭にTemplate:を付けた記事名でアクセスし、テンプレートが存在するかをチェック
783 string title = prefix + ":" + l.Title;
784 MediaWikiPage page = null;
787 page = this.From.GetPage(title) as MediaWikiPage;
789 catch (WebException e)
791 if (e.Status == WebExceptionStatus.ProtocolError
792 && (e.Response as HttpWebResponse).StatusCode != HttpStatusCode.NotFound)
794 // 記事が取得できない場合も、404でない場合は存在するとして処理
795 this.LogLine(String.Format(Resources.LogMessage_TemplateUnknown, l.Title, prefix, e.Message));
801 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.ReplaceTemplate > " + e.Message);
806 // 記事が存在する場合、テンプレートをつけた名前を使用
811 else if (l.IsSubpage)
814 l.Title = parent + l.Title;
817 // リンクを辿り、対象記事の言語間リンクを取得
818 string interWiki = this.GetInterWiki(l.Title, true);
820 // 記事自体が存在しない(赤リンク)場合、リンクはそのまま
821 StringBuilder b = new StringBuilder();
822 if (interWiki == null)
824 b.Append(l.OriginalText);
826 else if (interWiki == String.Empty)
828 // 言語間リンクが存在しない場合、[[:en:Template:xxx]]みたいな普通のリンクに置換
829 // おまけで、元のテンプレートの状態をコメントでつける
831 b.Append(this.From.Language.Code);
835 b.Append(l.OriginalText);
840 // 言語間リンクが存在する場合、そちらを指すように置換
851 b.Append(MediaWikiPage.Msgnw);
854 // : より前の部分を削除して出力(: が無いときは-1+1で0から)
855 b.Append(interWiki.Substring(interWiki.IndexOf(':') + 1));
864 foreach (string text in l.PipeTexts)
867 if (!String.IsNullOrEmpty(text))
869 // | の後に内部リンクやテンプレートが書かれている場合があるので、再帰的に処理する
870 b.Append(this.ReplaceText(text, parent));
878 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.replaceTemplate > " + l.OriginalText);
883 /// 指定されたインデックスの位置に存在する見出し(==関連項目==みたいなの)を解析し、可能であれば変換して返す。
885 /// <param name="heading">変換後の見出し。</param>
886 /// <param name="text">解析するテキスト。</param>
887 /// <param name="index">解析開始インデックス。</param>
888 /// <returns>見出しの場合、見出し終了位置のインデックスを返す。それ以外は-1。</returns>
889 protected virtual int ChkTitleLine(out string heading, string text, int index)
892 // ※見出しではない、構文がおかしいなどの場合、-1を返す
895 // 構文を解析して、1行の文字列と、=の個数を取得
896 // ※構文はWikipediaのプレビューで色々試して確認、足りなかったり間違ってたりするかも・・・
897 // ※Wikipediaでは <!--test-.=<!--test-.=関連項目<!--test-.==<!--test-. みたいなのでも
898 // 正常に認識するので、できるだけ対応する
899 // ※変換が正常に行われた場合、コメントは削除される
900 bool startFlag = true;
901 int startSignCounter = 0;
902 string nonCommentLine = String.Empty;
903 StringBuilder b = new StringBuilder();
904 for (lastIndex = index; lastIndex < text.Length; lastIndex++)
906 char c = text[lastIndex];
916 int subindex = MediaWikiTranslator.ChkComment(out comment, text, lastIndex);
920 lastIndex = subindex;
940 heading = b.ToString();
942 // 改行文字、または文章の最後+1になっているはずなので、1文字戻す
945 // = で始まる行ではない場合、処理対象外
946 if (startSignCounter < 1)
948 heading = String.Empty;
953 // ※↓の処理だと中身の無い行(====とか)は弾かれてしまうが、どうせ処理できないので許容する
954 int endSignCounter = 0;
955 for (int i = nonCommentLine.Length - 1; i >= startSignCounter; i--)
957 if (nonCommentLine[i] == '=')
967 // = で終わる行ではない場合、処理対象外
968 if (endSignCounter < 1)
970 heading = String.Empty;
974 // 始まりと終わり、=の少ないほうにあわせる(==test===とか用の処理)
975 int signCounter = startSignCounter;
976 if (startSignCounter > endSignCounter)
978 signCounter = endSignCounter;
982 string oldText = nonCommentLine.Substring(signCounter, nonCommentLine.Length - (signCounter * 2)).Trim();
983 string newText = this.GetHeading(oldText);
987 for (int i = 1; i < signCounter; i++)
992 string newHeading = sign + newText + sign;
993 this.LogLine(ENTER + heading + " " + Resources.RightArrow + " " + newHeading);
994 heading = newHeading;
998 this.LogLine(ENTER + heading);
1005 /// 指定されたコードでの見出しに相当する、別の言語での見出しを取得。
1007 /// <param name="heading">翻訳元言語での見出し。</param>
1008 /// <returns>翻訳先言語での見出し。値が存在しない場合は<c>null</c>。</returns>
1009 protected string GetHeading(string heading)
1011 return this.HeadingTable.GetWord(heading);
1015 /// 指定した言語での言語名称を ページ名|略称 の形式で取得。
1017 /// <param name="site">サイト。</param>
1018 /// <param name="code">言語のコード。</param>
1019 /// <returns>ページ名|略称形式の言語名称。</returns>
1020 protected string GetFullName(Website site, string code)
1022 if (site.Language.Names.ContainsKey(code))
1024 Language.LanguageName name = site.Language.Names[code];
1025 if (!String.IsNullOrEmpty(name.ShortName))
1027 return name.Name + "|" + name.ShortName;
1035 return String.Empty;
1039 /// 画像などのファイルへの内部リンクの置き換えを行う。
1041 /// <param name="link">内部リンク。</param>
1042 /// <returns>置き換え後のリンク文字列、置き換えを行わない場合<c>null</c>。</returns>
1043 private string ReplaceFileLink(MediaWikiPage.Link link)
1045 // 名前空間だけ翻訳先言語の書式に変換
1046 IList<string> names;
1047 if (!this.To.Namespaces.TryGetValue(this.To.FileNamespace, out names))
1049 // 翻訳先言語に相当する名前空間が無い場合、何もしない
1053 // 記事名の名前空間部分を置き換えて返す
1054 link.Title = names[0] + link.Title.Substring(link.Title.IndexOf(':'));