1 // ================================================================================================
3 // Wikipedia用の翻訳支援処理実装クラスソース</summary>
5 // <copyright file="MediaWikiTranslator.cs" company="honeplusのメモ帳">
6 // Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
9 // ================================================================================================
11 namespace Honememo.Wptscs.Logics
14 using System.Collections.Generic;
19 using System.Windows.Forms;
20 using Honememo.Parsers;
21 using Honememo.Utilities;
22 using Honememo.Wptscs.Models;
23 using Honememo.Wptscs.Parsers;
24 using Honememo.Wptscs.Properties;
25 using Honememo.Wptscs.Utilities;
26 using Honememo.Wptscs.Websites;
29 /// Wikipedia用の翻訳支援処理実装クラスです。
31 public class MediaWikiTranslator : Translator
38 public new MediaWiki From
42 return base.From as MediaWiki;
54 public new MediaWiki To
58 return base.To as MediaWiki;
73 /// ※継承クラスでは、この関数に処理を実装すること
75 /// <param name="name">記事名。</param>
76 /// <exception cref="ApplicationException">処理が中断された場合。中断の理由は<see cref="Translator.Log"/>に出力される。</exception>
77 protected override void RunBody(string name)
80 MediaWikiPage article = this.GetTargetPage(name);
83 throw new ApplicationException("article is not found");
86 // 対象記事に言語間リンクが存在する場合、処理を継続するか確認
87 // ※ 言語間リンク取得中は、処理状態を解析中に変更
88 string interWiki = null;
89 this.ChangeStatusInExecuting(
90 () => interWiki = article.GetInterWiki(this.To.Language.Code),
91 Resources.StatusParsing);
92 if (!String.IsNullOrEmpty(interWiki))
94 // ※ 確認ダイアログの表示中は処理時間をカウントしない
95 this.Stopwatch.Stop();
97 String.Format(Resources.QuestionMessageArticleExisted, interWiki),
98 Resources.QuestionTitle,
99 MessageBoxButtons.YesNo,
100 MessageBoxIcon.Question)
103 this.LogLine(ENTER + String.Format(Resources.QuestionMessageArticleExisted, interWiki));
104 throw new ApplicationException("user canceled");
108 this.Stopwatch.Start();
109 this.LogLine(Resources.RightArrow + " " + String.Format(Resources.LogMessageTargetArticleHadInterWiki, interWiki));
113 this.Text += this.CreateOpening(article.Title);
115 // 言語間リンク・定型句の変換、実行中は処理状態を解析中に設定
116 this.LogLine(ENTER + Resources.RightArrow + " " + String.Format(Resources.LogMessageStartParseAndReplace, interWiki));
117 this.ChangeStatusInExecuting(
118 () => this.Text += this.ReplaceElement(new MediaWikiParser(this.From).Parse(article.Text), article.Title).ToString(),
119 Resources.StatusParsing);
121 // 記事の末尾に新しい言語間リンクと、コメントを追記
122 this.Text += this.CreateEnding(article);
124 // ダウンロードされるテキストがLFなので、最後に全てCRLFに変換
125 // ※ダウンロード時にCRLFにするような仕組みが見つかれば、そちらを使う
126 // その場合、上のように\nをべたに吐いている部分を修正する
127 this.Text = this.Text.Replace("\n", ENTER);
132 #region 他のクラスの処理をこのクラスにあわせて拡張したメソッド
135 /// ログメッセージを出力しつつページを取得。
137 /// <param name="title">ページタイトル。</param>
138 /// <param name="notFoundMsg">取得できない場合に出力するメッセージ。</param>
139 /// <returns>取得したページ。ページが存在しない場合は <c>null</c> を返す。</returns>
140 /// <remarks>通信エラーなど例外が発生した場合は、別途エラーログを出力する。</remarks>
141 protected new MediaWikiPage GetPage(string title, string notFoundMsg)
143 // & 等の特殊文字をデコードして、親クラスのメソッドを呼び出し
144 return base.GetPage(WebUtility.HtmlDecode(title), notFoundMsg) as MediaWikiPage;
149 #region 冒頭/末尾ブロックの生成メソッド
152 /// 変換後記事冒頭用の「'''日本語記事名'''([[英語|英]]: '''英語記事名''')」みたいなのを作成する。
154 /// <param name="title">翻訳支援対象の記事名。</param>
155 /// <returns>冒頭部のテキスト。</returns>
156 protected virtual string CreateOpening(string title)
158 StringBuilder b = new StringBuilder("'''xxx'''");
159 string langPart = String.Empty;
160 string langTitle = this.GetFullName(this.From, this.To.Language.Code);
161 if (!String.IsNullOrEmpty(langTitle))
163 langPart = new MediaWikiLink(langTitle).ToString() + ": ";
166 b.Append(this.To.Language.FormatBracket(langPart + "'''" + title + "'''"));
172 /// 変換後記事末尾用の新しい言語間リンクとコメントを作成する。
174 /// <param name="page">翻訳支援対象の記事。</param>
175 /// <returns>末尾部のテキスト。</returns>
176 protected virtual string CreateEnding(MediaWikiPage page)
178 MediaWikiLink link = new MediaWikiLink();
179 link.Title = page.Title;
180 link.Code = this.From.Language.Code;
181 return "\n\n" + link.ToString() + "\n" + String.Format(
182 Resources.ArticleFooter,
183 FormUtils.ApplicationName(),
184 this.From.Language.Code,
186 page.Timestamp.HasValue ? page.Timestamp.Value.ToString("U") : String.Empty) + "\n";
191 #region 言語間リンクの取得メソッド
194 /// 指定された記事を取得し、言語間リンクを確認、返す(テンプレート以外)。
196 /// <param name="name">記事名。</param>
197 /// <returns>言語間リンク先の記事、存在しない場合 <c>null</c>。</returns>
198 protected string GetInterWiki(string name)
200 return this.GetInterWiki(name, false);
204 /// 指定された記事を取得し、言語間リンクを確認、返す。
206 /// <param name="title">記事名。</param>
207 /// <param name="template"><c>true</c> テンプレート。</param>
208 /// <returns>言語間リンク先の記事、存在しない場合 <c>null</c>。</returns>
209 protected string GetInterWiki(string title, bool template)
211 MediaWikiLink element;
214 element = new MediaWikiTemplate(title);
218 element = new MediaWikiLink(title);
221 Log += element.ToString() + " " + Resources.RightArrow + " ";
222 string interWiki = this.GetInterWikiUseTable(title, this.To.Language.Code);
224 // 改行が出力されていない場合(正常時)、改行
225 if (!Log.EndsWith(ENTER))
234 /// ログメッセージを出力しつつ、指定された記事の指定された言語コードへの言語間リンクを返す。
236 /// <param name="title">記事名。</param>
237 /// <param name="code">言語コード。</param>
238 /// <returns>言語間リンク先の記事名。見つからない場合は空。ページ自体が存在しない場合は<c>null</c>。</returns>
239 /// <remarks>対訳表が指定されている場合、その内容を使用する。また取得結果を対訳表に追加する。</remarks>
240 protected string GetInterWikiUseTable(string title, string code)
242 if (this.ItemTable == null)
244 // 対訳表が指定されていない場合は、普通に記事を取得
245 return this.GetInterWiki(title, code);
248 string interWiki = null;
249 lock (this.ItemTable)
252 // ※ 対訳表へのキーとしてはHTMLデコードした記事名を使用する
253 TranslationDictionary.Item item;
254 if (this.ItemTable.TryGetValue(WebUtility.HtmlDecode(title), out item))
257 // リダイレクトがあれば、そのメッセージも表示
258 if (!String.IsNullOrWhiteSpace(item.Alias))
260 this.Log += Resources.LogMessageRedirect + " "
261 + new MediaWikiLink(item.Alias).ToString() + " " + Resources.RightArrow + " ";
264 if (!String.IsNullOrEmpty(item.Word))
266 interWiki = item.Word;
267 Log += new MediaWikiLink(interWiki).ToString();
271 interWiki = String.Empty;
272 Log += Resources.LogMessageInterWikiNotFound;
276 Log += Resources.LogMessageNoteTranslation;
280 // 対訳表に存在しない場合は、普通に取得し表に記録
281 // ※ nullも存在しないことの記録として格納
282 item = new TranslationDictionary.Item { Timestamp = DateTime.UtcNow };
283 MediaWikiPage page = this.GetPage(title, Resources.LogMessageLinkArticleNotFound);
285 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
286 if (page != null && page.IsRedirect())
288 item.Alias = page.Redirect.Title;
289 this.Log += Resources.LogMessageRedirect + " "
290 + new MediaWikiLink(page.Redirect.Title).ToString() + " " + Resources.RightArrow + " ";
291 page = this.GetPage(page.Redirect.Title, Resources.LogMessageLinkArticleNotFound);
297 interWiki = page.GetInterWiki(this.To.Language.Code);
298 if (!String.IsNullOrEmpty(interWiki))
300 Log += new MediaWikiLink(interWiki).ToString();
304 Log += Resources.LogMessageInterWikiNotFound;
307 item.Word = interWiki;
308 this.ItemTable[WebUtility.HtmlDecode(title)] = item;
316 /// ログメッセージを出力しつつ、指定された記事の指定された言語コードへの言語間リンクを返す。
318 /// <param name="title">記事名。</param>
319 /// <param name="code">言語コード。</param>
320 /// <returns>言語間リンク先の記事名。見つからない場合は空。ページ自体が存在しない場合は<c>null</c>。</returns>
321 protected string GetInterWiki(string title, string code)
323 MediaWikiPage page = this.GetPage(title, Resources.LogMessageLinkArticleNotFound);
325 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
326 if (page != null && page.IsRedirect())
328 this.Log += Resources.LogMessageRedirect + " "
329 + new MediaWikiLink(page.Redirect.Title).ToString() + " " + Resources.RightArrow + " ";
330 page = this.GetPage(page.Redirect.Title, Resources.LogMessageLinkArticleNotFound);
334 string interWiki = null;
337 interWiki = page.GetInterWiki(this.To.Language.Code);
338 if (!String.IsNullOrEmpty(interWiki))
340 Log += new MediaWikiLink(interWiki).ToString();
344 Log += Resources.LogMessageInterWikiNotFound;
358 /// <param name="element">ページ要素。</param>
359 /// <param name="parent">サブページ用の親記事タイトル。</param>
360 /// <returns>変換後のページ要素。</returns>
361 protected virtual IElement ReplaceElement(IElement element, string parent)
364 this.ThrowExceptionIfCanceled();
366 // 要素の型に応じて、必要な置き換えを行う
367 if (element is MediaWikiTemplate)
370 return this.ReplaceTemplate((MediaWikiTemplate)element, parent);
372 else if (element is MediaWikiLink)
375 return this.ReplaceLink((MediaWikiLink)element, parent);
377 else if (element is MediaWikiHeading)
380 return this.ReplaceHeading((MediaWikiHeading)element, parent);
382 else if (element is MediaWikiVariable)
385 return this.ReplaceVariable((MediaWikiVariable)element, parent);
387 else if (element is ListElement)
390 return this.ReplaceListElement((ListElement)element, parent);
393 // それ以外は、特に何もせず元の値を返す
398 /// 内部リンクを解析し、変換先言語の記事へのリンクに変換する。
400 /// <param name="link">変換元リンク。</param>
401 /// <param name="parent">サブページ用の親記事タイトル。</param>
402 /// <returns>変換済みリンク。</returns>
403 protected virtual IElement ReplaceLink(MediaWikiLink link, string parent)
405 // 記事名が存在しないor自記事内の別セクションへのリンクの場合、記事名絡みの処理を飛ばす
406 if (!this.IsSectionLink(link, parent))
409 MediaWikiPage article = new MediaWikiPage(this.From, link.Title);
414 link.Title = parent + link.Title;
416 else if (!String.IsNullOrEmpty(link.Code))
418 // 言語間リンク・姉妹プロジェクトへのリンクの場合、変換対象外とする
419 // ただし、先頭が : でない、翻訳先言語への言語間リンクだけは削除
420 return this.ReplaceLinkInterwiki(link);
422 else if (article.IsFile())
424 // 画像の場合、名前空間を翻訳先言語の書式に変換、パラメータ部を再帰的に処理
425 return this.ReplaceLinkFile(link, parent);
427 else if (article.IsCategory() && !link.IsColon)
429 // カテゴリで記事へのリンクでない([[:Category:xxx]]みたいなリンクでない)場合、
431 return this.ReplaceLinkCategory(link);
434 // 専用処理の無い内部リンクの場合、言語間リンクによる置き換えを行う
435 string interWiki = this.GetInterWiki(link.Title);
436 if (interWiki == null)
438 // 記事自体が存在しない(赤リンク)場合、リンクはそのまま
440 else if (interWiki == String.Empty)
442 // 言語間リンクが存在しない場合、可能なら{{仮リンク}}に置き換え
443 if (!String.IsNullOrEmpty(this.To.LinkInterwikiFormat))
445 return this.ReplaceLinkLinkInterwiki(link);
448 // 設定が無ければ [[:en:xxx]] みたいな形式に置換
449 link.Title = this.From.Language.Code + ':' + link.Title;
452 else if (link.IsSubpage)
454 // 言語間リンクが存在してサブページの場合、親ページ部分を消す
455 link.Title = StringUtils.Substring(interWiki, interWiki.IndexOf('/'));
459 // 普通に言語間リンクが存在する場合、記事名を置き換え
460 link.Title = interWiki;
463 if (link.PipeTexts.Count == 0 && interWiki != null)
465 // 表示名が存在しない場合、元の名前を表示名に設定
468 new TextElement(new MediaWikiLink { Title = article.Title, Section = link.Section }
473 // セクション部分([[#関連項目]]とか)を変換
474 if (!String.IsNullOrEmpty(link.Section))
476 link.Section = this.ReplaceLinkSection(link.Section);
479 link.ParsedString = null;
484 /// テンプレートを解析し、変換先言語の記事へのテンプレートに変換する。
486 /// <param name="template">変換元テンプレート。</param>
487 /// <param name="parent">サブページ用の親記事タイトル。</param>
488 /// <returns>変換済みテンプレート。</returns>
489 protected virtual IElement ReplaceTemplate(MediaWikiTemplate template, string parent)
491 // システム変数({{PAGENAME}}とか)の場合は対象外
492 if (this.From.IsMagicWord(template.Title))
497 // テンプレートは通常名前空間が省略されているので補完する
498 string filledTitle = this.FillTemplateName(template, parent);
500 // リンクを辿り、対象記事の言語間リンクを取得
501 string interWiki = this.GetInterWiki(filledTitle, true);
502 if (interWiki == null)
504 // 記事自体が存在しない(赤リンク)場合、リンクはそのまま
507 else if (interWiki == String.Empty)
509 // 言語間リンクが存在しない場合、[[:en:Template:xxx]]みたいな普通のリンクに置換
510 // おまけで、元のテンプレートの状態をコメントでつける
511 ListElement list = new ListElement();
512 MediaWikiLink link = new MediaWikiLink();
514 link.Title = this.From.Language.Code + ':' + filledTitle;
516 XmlCommentElement comment = new XmlCommentElement();
517 comment.Raw = ' ' + template.ToString() + ' ';
523 // 言語間リンクが存在する場合、そちらを指すように置換
524 // : より前の部分を削除して出力(: が無いときは-1+1で0から)
525 template.Title = interWiki.Substring(interWiki.IndexOf(':') + 1);
527 // | の後に内部リンクやテンプレートが書かれている場合があるので、再帰的に処理する
528 template.PipeTexts = this.ReplaceElements(template.PipeTexts, parent);
529 template.ParsedString = null;
535 /// 指定された見出しに対して、対訳表による変換を行う。
537 /// <param name="heading">見出し。</param>
538 /// <param name="parent">サブページ用の親記事タイトル。</param>
539 /// <returns>変換後の見出し。</returns>
540 protected virtual IElement ReplaceHeading(MediaWikiHeading heading, string parent)
543 StringBuilder oldText = new StringBuilder();
544 foreach (IElement e in heading)
546 oldText.Append(e.ToString());
549 string oldHeading = heading.ToString();
550 string newText = this.GetHeading(oldText.ToString().Trim());
553 // 対訳表による変換が行えた場合、そこで処理終了
555 heading.ParsedString = null;
556 heading.Add(new XmlTextElement(newText));
557 this.LogLine(ENTER + oldHeading + " " + Resources.RightArrow + " " + heading.ToString());
561 // 対訳表に存在しない場合、内部要素を通常の変換で再帰的に処理
562 this.LogLine(ENTER + heading.ToString());
563 return this.ReplaceListElement(heading, parent);
567 /// 変数要素を再帰的に解析し、変換先言語の記事への要素に変換する。
569 /// <param name="variable">変換元変数要素。</param>
570 /// <param name="parent">サブページ用の親記事タイトル。</param>
571 /// <returns>変換済み変数要素。</returns>
572 protected virtual IElement ReplaceVariable(MediaWikiVariable variable, string parent)
574 // 変数、これ自体は処理しないが、再帰的に探索
575 string old = variable.Value.ToString();
576 variable.Value = this.ReplaceElement(variable.Value, parent);
577 if (variable.Value.ToString() != old)
579 // 内部要素が変化した(置き換えが行われた)場合、変換前のテキストを破棄
580 variable.ParsedString = null;
587 /// 要素を再帰的に解析し、変換先言語の記事への要素に変換する。
589 /// <param name="listElement">変換元要素。</param>
590 /// <param name="parent">サブページ用の親記事タイトル。</param>
591 /// <returns>変換済み要素。</returns>
592 protected virtual IElement ReplaceListElement(ListElement listElement, string parent)
594 // 値を格納する要素、これ自体は処理しないが、再帰的に探索
595 for (int i = 0; i < listElement.Count; i++)
597 string old = listElement[i].ToString();
598 listElement[i] = this.ReplaceElement(listElement[i], parent);
599 if (listElement[i].ToString() != old)
601 // 内部要素が変化した(置き換えが行われた)場合、変換前のテキストを破棄
602 listElement.ParsedString = null;
616 /// <param name="title">翻訳支援対象の記事名。</param>
617 /// <returns>取得したページ。取得失敗時は<c>null</c>。</returns>
618 protected MediaWikiPage GetTargetPage(string title)
620 // 指定された記事のXMLデータをWikipediaから取得
621 this.LogLine(String.Format(Resources.LogMessageGetTargetArticle, this.From.Location, title));
622 MediaWikiPage page = this.GetPage(title, Resources.RightArrow + " " + Resources.LogMessageTargetArticleNotFound);
624 // リダイレクトかをチェックし、リダイレクトであれば、その先の記事を取得
625 if (page != null && page.IsRedirect())
627 this.LogLine(Resources.RightArrow + " " + Resources.LogMessageRedirect
628 + " " + new MediaWikiLink(page.Redirect.Title).ToString());
631 Resources.RightArrow + " " + Resources.LogMessageTargetArticleNotFound);
638 /// 同記事内の別のセクションを指すリンク([[#関連項目]]とか[[自記事#関連項目]]とか)か?
640 /// <param name="link">判定する内部リンク。</param>
641 /// <param name="parent">内部リンクがあった記事。</param>
642 /// <returns>セクション部分のみ変換済みリンク。</returns>
643 private bool IsSectionLink(MediaWikiLink link, string parent)
645 // 記事名が指定されていない、または記事名が自分の記事名で
646 // 言語コード等も特に無く、かつセクションが指定されている場合
647 // (記事名もセクションも指定されていない・・・というケースもありえるが、
648 // その場合他に指定できるものも思いつかないので通す)
649 return String.IsNullOrEmpty(link.Title)
650 || (link.Title == parent && String.IsNullOrEmpty(link.Code) && !String.IsNullOrEmpty(link.Section));
654 /// 内部リンクのセクション部分([[#関連項目]]とか)の定型句変換を行う。
656 /// <param name="section">セクション文字列。</param>
657 /// <returns>セクション部分のみ変換済みリンク。</returns>
658 private string ReplaceLinkSection(string section)
660 // セクションが指定されている場合、定型句変換を通す
661 string heading = this.GetHeading(section);
662 return heading != null ? heading : section;
666 /// 言語間リンク指定の内部リンクを解析し、不要であれば削除する。
668 /// <param name="link">変換元言語間リンク。</param>
669 /// <returns>変換済み言語間リンク。</returns>
670 private IElement ReplaceLinkInterwiki(MediaWikiLink link)
672 // 言語間リンク・姉妹プロジェクトへのリンクの場合、変換対象外とする
673 // ただし、先頭が : でない、翻訳先言語への言語間リンクだけは削除
674 if (!link.IsColon && link.Code == this.To.Language.Code)
676 return new TextElement();
683 /// カテゴリ指定の内部リンクを解析し、変換先言語のカテゴリへのリンクに変換する。
685 /// <param name="link">変換元カテゴリ。</param>
686 /// <returns>変換済みカテゴリ。</returns>
687 private IElement ReplaceLinkCategory(MediaWikiLink link)
689 // リンクを辿り、対象記事の言語間リンクを取得
690 string interWiki = this.GetInterWiki(link.Title);
691 if (interWiki == null)
693 // 記事自体が存在しない(赤リンク)場合、リンクはそのまま
696 else if (interWiki == String.Empty)
698 // 言語間リンクが存在しない場合、コメントで元の文字列を保存した後
699 // [[:en:xxx]]みたいな形式に置換。また | 以降は削除する
700 XmlCommentElement comment = new XmlCommentElement();
701 comment.Raw = ' ' + link.ToString() + ' ';
703 link.Title = this.From.Language.Code + ':' + link.Title;
705 link.PipeTexts.Clear();
706 link.ParsedString = null;
708 ListElement list = new ListElement();
715 // 普通に言語間リンクが存在する場合、記事名を置き換え
716 link.Title = interWiki;
717 link.ParsedString = null;
723 /// ファイル指定の内部リンクを解析し、変換先言語で参照可能なファイルへのリンクに変換する。
725 /// <param name="link">変換元リンク。</param>
726 /// <param name="parent">サブページ用の親記事タイトル。</param>
727 /// <returns>変換済みリンク。</returns>
728 private IElement ReplaceLinkFile(MediaWikiLink link, string parent)
730 // 名前空間を翻訳先言語の書式に変換、またパラメータ部を再帰的に処理
731 link.Title = this.ReplaceLinkNamespace(link.Title, this.To.FileNamespace);
732 link.PipeTexts = this.ReplaceElements(link.PipeTexts, parent);
733 link.ParsedString = null;
738 /// 記事名のうち名前空間部分の変換先言語への変換を行う。
740 /// <param name="title">変換元記事名。</param>
741 /// <param name="id">名前空間のID。</param>
742 /// <returns>変換済み記事名。</returns>
743 private string ReplaceLinkNamespace(string title, int id)
747 if (!this.To.Namespaces.TryGetValue(id, out names))
749 // 翻訳先言語に相当する名前空間が無い場合、何もしない
753 // 記事名の名前空間部分を置き換えて返す
754 return names[0] + title.Substring(title.IndexOf(':'));
758 /// 内部リンクを他言語版への{{仮リンク}}等に変換する。。
760 /// <param name="link">変換元言語間リンク。</param>
761 /// <returns>変換済み言語間リンク。</returns>
762 private IElement ReplaceLinkLinkInterwiki(MediaWikiLink link)
764 // 仮リンクにはセクションの指定が可能なので、存在する場合付加する
765 // ※ 渡されたlinkをそのまま使わないのは、余計なゴミが含まれる可能性があるため
766 MediaWikiLink title = new MediaWikiLink { Title = link.Title, Section = link.Section };
767 string langTitle = title.GetLinkString();
768 if (!String.IsNullOrEmpty(title.Section))
770 // 変換先言語版のセクションは、セクションの変換を通したものにする
771 title.Section = this.ReplaceLinkSection(title.Section);
774 // 表示名は、設定されていればその値を、なければ変換元言語の記事名を使用
775 string label = langTitle;
776 if (link.PipeTexts.Count > 0)
778 label = link.PipeTexts.Last().ToString();
782 // ※ {{仮リンク}}を想定しているが、やろうと思えば何でもできるのでテキストで処理
783 return new TextElement(this.To.FormatLinkInterwiki(title.GetLinkString(), this.From.Language.Code, langTitle, label));
787 /// 渡された要素リストに対して<see cref="ReplaceElement"/>による変換を行う。
789 /// <param name="elements">変換元要素リスト。</param>
790 /// <param name="parent">サブページ用の親記事タイトル。</param>
791 /// <returns>変換済み要素リスト。</returns>
792 private IList<IElement> ReplaceElements(IList<IElement> elements, string parent)
794 if (elements == null)
799 IList<IElement> result = new List<IElement>();
800 foreach (IElement e in elements)
802 result.Add(this.ReplaceElement(e, parent));
809 /// テンプレート名に必要に応じて名前空間を補完する。
811 /// <param name="template">テンプレート。</param>
812 /// <param name="parent">サブページ用の親記事タイトル。</param>
813 /// <returns>補完済みのテンプレート名。</returns>
814 private string FillTemplateName(MediaWikiTemplate template, string parent)
816 if (template.IsColon || !new MediaWikiPage(this.From, template.Title).IsMain())
818 // 標準名前空間が指定されている(先頭にコロンが無い)
819 // または何かしらの名前空間が指定されている場合、補完不要
820 return template.Title;
822 else if (template.IsSubpage)
824 // サブページの場合、親記事名での補完のみ
825 return parent + template.Title;
828 // 補完する必要がある場合、名前空間のプレフィックス(Template等)を取得
829 string prefix = this.GetTemplatePrefix();
830 if (String.IsNullOrEmpty(prefix))
832 // 名前空間の設定が存在しない場合、何も出来ないため終了
833 return template.Title;
836 // 頭にプレフィックスを付けた記事名で実在するかをチェック
837 string filledTitle = prefix + ":" + template.Title;
839 // 既に対訳表にプレフィックス付きの記事名が確認されているか?
840 // ※ 対訳表へのキーとしてはHTMLデコードした記事名を使用する
841 if (this.ItemTable != null && this.ItemTable.ContainsKey(WebUtility.HtmlDecode(filledTitle)))
843 // 記事が存在する場合、プレフィックスをつけた名前を使用
847 // 未確認の場合、実際に頭にプレフィックスを付けた記事名でアクセスし、存在するかをチェック
848 // TODO: GetInterWikiの方とあわせ、テンプレートでは2度GetPageが呼ばれている。可能であれば共通化する
849 MediaWikiPage page = null;
852 page = this.From.GetPage(filledTitle) as MediaWikiPage;
854 catch (WebException e)
856 if (e.Status == WebExceptionStatus.ProtocolError
857 && (e.Response as HttpWebResponse).StatusCode != HttpStatusCode.NotFound)
859 // 記事が取得できない場合も、404でない場合は存在するものとして処理
860 this.LogLine(String.Format(Resources.LogMessageTemplateNameUnidentified, template.Title, prefix, e.Message));
866 // それ以外のエラー(Webではなくfileでのエラーとか)は存在しないものと扱う
867 System.Diagnostics.Debug.WriteLine("MediaWikiTranslator.FillTemplateName > " + e.Message);
872 // 記事が存在する場合、プレフィックスをつけた名前を使用
876 return template.Title;
880 /// テンプレート名前空間のプレフィックスを取得。
882 /// <returns>プレフィックス。取得できない場合<c>null</c></returns>
883 private string GetTemplatePrefix()
885 IList<string> prefixes = this.From.Namespaces[this.From.TemplateNamespace];
886 if (prefixes != null)
888 return prefixes.FirstOrDefault();
895 /// 指定されたコードでの見出しに相当する、別の言語での見出しを取得。
897 /// <param name="heading">翻訳元言語での見出し。</param>
898 /// <returns>翻訳先言語での見出し。値が存在しない場合は<c>null</c>。</returns>
899 private string GetHeading(string heading)
901 return this.HeadingTable.GetWord(heading);
905 /// 指定した言語での言語名称を ページ名|略称 の形式で取得。
907 /// <param name="site">サイト。</param>
908 /// <param name="code">言語のコード。</param>
909 /// <returns>ページ名|略称形式の言語名称。</returns>
910 private string GetFullName(Website site, string code)
912 if (site.Language.Names.ContainsKey(code))
914 Language.LanguageName name = site.Language.Names[code];
915 if (!String.IsNullOrEmpty(name.ShortName))
917 return name.Name + "|" + name.ShortName;