воскресенье, 20 ноября 2016 г.

О том, как можно генерировать локализованные версии справки

Данная заметка рассказывает о том, как можно генерировать локализованные версии справочной системы на основе нашего программного кода (в данном случае написанном на C#). Если кто-то знает более удобный способ - с интересом почитаю о нём в комментариях к этой заметке.

 Введение
Как известно, исходными данными для генерации справки являются XML-файлы, генерируемые MS Visual Studio. Эти файлы создаются в том случае, если в настройках нашего проекта, на вкладке Build установлена галочка XML documentation file. Эти же файлы используются технологией IntelliSense для того, чтобы выдавать всплывающие подсказки в редакторе кода, являясь весьма удобной и всеми нами любимой особенностью. 

 Обозначенные выше XML-файлы генерируются на основе специально оформленных комментариев, присутствующих в нашем коде. Такими комментариями обычно помечаются классы, свойства, методы, делегаты, события, структуры и перечисления. Полный перечень допустимых XML-тегов с примерами их использования опубликован в MSDN.

Для того, чтобы иметь возможность генерировать документацию для разных локализаций (например для русской и английской), XML-комментарии следует выносить во внешние файлы, вместо того, чтобы размещать их непосредственно в наших файлах исходного кода. Более того, для каждой интересующей нас локализации в составе нашего решения следует создавать отдельный проект. Такой способ, помимо прочего, способствует разделению процесса разработки приложения от разработки документации. Т.е. в то время пока один разработчик пишет программный код, другой в это же время может параллельно заниматься составлением русскоязычной документации, а третий - англоязычной.

Например, если целевой проект имеет имя HelloDocs, то для русской и английской версии справочной системы мы добавим в наш проект два новых решения - HelloDocs.Ru и HelloDocs.En (на основе шаблона Class Library). В этих проектах мы будем размещать все XML-файлы: в HelloDocs.En - англоязычная версия, а в HelloDocs.Ru - русскоязычная.
 
Затем в настройках обоих проектов следует указать один и тот же(!!!) каталог вывода: в настройках проекта, на вкладке Build в свойстве Output path. А для всех добавляемых (в дальнейшем) нами в эти проекты XML-файлов свойству Copy to Output Directory следует обязательно назначать значение copy always.

Вот пример исходного кода, определённого в составе файла Magic.cs проекта HelloDocs:

namespace HelloDocs {



    /// <include file='doc/Magic.xml'

    /// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>

    public class Magic {

        /// <include file='doc/Magic.xml'

        /// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>

        public void Foo() { }

    }

}

Соответствующие ему XML-файлы с комментариями будут показаны ниже в данной заметке.
Обратите внимание на то, что в атрибутах file элементов include следует указывать файлы, размещённые не в каталогах локализованных проектов, созданных нами ранее, но файлы размещённые в директории, которую мы выше указали в качестве выходного каталога для локализованных проектов, добавленных нами. На данный момент в том каталоге ничего нет, но это временно, т.к. в процессе компиляции, нужные XML-файлы будут копироваться в тот каталог и Visual Studio найдёт их там в процессе сборки нашего проекта HelloDocs.

Поскольку теперь для нас становился важен порядок компиляции проектов, то мы должны указать, что наш целевой проект зависит от локализованных проектов справочной системы. Соответственно они должны собираться раньше него:


Затем в Менеджере Конфигураций для всех проектов локализованных справочных систем следует снять галочки, оставив только для одного из них. Поскольку в нашем примере локализованных версий будет две, то снимаем галочку только для одного проекта:


 
В проект HelloDocs.En добавим файл Magic.xml с таким содержимым:

<?xml version="1.0" encoding="utf-8" ?>

<Documentation>

  <Member Name="HelloDocs.Magic">

    <summary>

      The <c>HelloDocs.Magic</c> is super-class...

    </summary>

    <remarks>

      Late it will be very popularly :)

    </remarks>

  </Member>

  <Member Name="HelloDocs.Magic.Foo()">

    <summary>

      The super-method...

    </summary>

    <remarks>It does nothing still... :)</remarks>

  </Member>

</Documentation>

В проект HelloDocs.Ru добавим файл Magic.xml с таким содержимым:

<?xml version="1.0" encoding="utf-8" ?>

<Documentation>

  <Member Name="HelloDocs.Magic">

    <summary>

      Наш <c>HelloDocs.Magic</c> есть супер-класс...

    </summary>

    <remarks>

      Позднее он будет очень популярен! :)

    </remarks>

  </Member>

  <Member Name="HelloDocs.Magic.Foo()">

    <summary>

      Это супер-метод...

    </summary>

    <remarks>Пока он ничего не делает... :)</remarks>

  </Member>

</Documentation>

Не забываем для этих XML-файлов изменить значение свойства Copy to Output Directory, как было указано выше. Обратите внимание, что эти файлы имеют одинаковые имена и одинаковую XML-структуру. Различие состоит только в содержимом соответствующих XML-элементов (это важно!!!).

Теперь наше решение выглядит следующим образом:


XML-файлы, генерируемые Visual Studio на основе XML-файлов наших локализованных проектов теперь могут использоваться в Sandcastle для генерации конечной документации.

Если Sandcastle ещё не установлен на вашей машине, то теперь самое время это сделать. Для установки Sancastle требуются административные права. После установки, в перечне доступных типов проектов IDE появляется дополнительный тип проекта, при помощи которого можно генерировать справку для интересующего нас проекта. Добавим в наше решение новый проект, созданный на основе нужного нам шаблона и присвоим ему имя HelpDocs.Doc:




Создав проект справки, первым делом мы указываем в его настройках источник, откуда нужно будет получить данные для формирования документации:



Интересующий нас формат справки указывается в настройках нашего Sandcastle-проекта:

Теперь запустив сборку нашего решения мы получим не только наш DLL, но и сгенерированный файл справки:



На скрине красной линией подчёркнут английский текст нашего XML-файла. Теперь отключим сборку англоязычной справки и включим русскоязычную:




Затем в настройках проекта HelpDocs.Doc меняем свойство Help file language, выбрав в нём русскую локализацию (чтобы дополнительный текст, присутствующий в документации, был так же на русском языке):



Теперь снова запускаем сборку нашего решения и проверяем сгенерированный файл справки:



Как мы видим - теперь мы получили русский вариант справки. Т.о. при желании мы можем реализовать столько локализаций нашей справочной системы, сколько посчитаем нужным.

P.S.
В идеале, конечно же, было бы избавиться от необходимости переключения галочек и правки свойства нашего Sandcastle-проекта, путём создания такого проекта отдельно под каждую локализацию. Но проблема заключается в том, что в нашем исходном коде жёстко прописан каталог, в котором следует искать XML-файл.

Конечно, можно было бы решить эту проблему при помощи конструкций #if/#elif/#endif:

namespace HelloDocs {

#if ENU
    /// <include file='Magic.doc.enu.xml' 
    /// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>
#elif RUS
    /// <include file='Magic.doc.rus.xml' 
    /// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>
#endif
    public class Magic {
#if ENU
        /// <include file='Magic.doc.enu.xml' 
        /// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>
#elif RUS
        /// <include file='Magic.doc.rus.xml' 
        /// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>
#endif
        public void Foo() { }
    }
}

Однако писать такие конструкции - процесс весьма трудоёмкий. К тому же, в случае добавления новой локализации придётся везде в коде добавлять дополнительный блок #elif, что может оказаться очень затратным по времени для больших и даже средних проектов.

Дополнительные ресурсы

Комментариев нет: