LOCALE::PO4A::TRANSTRACTOR.3PM(1)

НАЗВАНИЕ
Locale::Po4a::TransTractor - обобщённый экстрактор переводов (от Trans[lator ex]Tractor).

ОПИСАНИЕ
Целью проекта po4a (PO for anything, PO везде и для всего) является облегчение процесса перевода (и что более важно — поддержки перевода), используя инструменты gettext в тех случаях, когда их применение может выглядеть неожиданным, например для документации.

Данный класс является предком каждого парсера po4a, используемого для анализа документа, поиска переводимых строк, извлечения их в PO-файл и замены их переводом в итоговом документе.

Более формально, он принимает на вход следующие аргументы:

-   документ для перевода;

-   PO-файл, содержащий перевод.

На выходе он производит:

-   другой PO-файл, как результат извлечения переводимых строк из входного документа;

-   переведённый документ с той же структурой, что и входной, но все переводимые строки заменены переводами, найденными во входном PO-файле.

Ниже приведено графическое представление этого процесса:

   Входной документ --\                             /---> Выходной документ
                      \                           /       (переведённый)
                       +-> функция parse() ------+
                      /                           \
  Входной PO --------/                             \---> Выходной PO
                                                          (извлечённый)

ФУНКЦИИ, КОТОРЫЕ ДОЛЖНЫ БЫТЬ ПЕРЕОПРЕДЕЛЕНЫ В ВАШЕМ ПАРСЕРЕ

parse()
    Именно здесь происходит вся работа: разбор входного документа, генерация выходного документа и извлечение переводимых строк. Это довольно просто сделать, используя предоставленные функции, описанные ниже в разделе ВНУТРЕННИЕ ФУНКЦИИ. См. также пример в разделе КРАТКОЕ ОПИСАНИЕ.

    Эта функция вызывается из функции process() описанной ниже, но если вы решите использовать функцию new() и вручную добавите содержимое в ваш документ, вам придется вызывать эту функцию самостоятельно.

docheader()
    Эта функция возвращает заголовок, который мы должны добавить к созданному документу. Правильно процитированный, он будет комментарием на целевом языке. Для чего это нужно, см. раздел Educating developers about translations в po4a(7).

КРАТКОЕ СОДЕРЖАНИЕ
В следующем примере анализируется список абзацев, начинающихся с "<p>". Для простоты мы предполагаем, что документ хорошо отформатирован, т.е в нем присутствуют только теги '<p>', и каждый такой тег находится в начале абзаца.

 sub parse {
   my $self = shift;

   PARAGRAPH: while (1) {
       my ($paragraph,$pararef)=("","");
       my $first=1;
       my ($line,$lref)=$self->shiftline();
       while (defined($line)) {
           if ($line =~ m/<p>/ && !$first--; ) {
               # Это не первый раз, когда мы видим <p>.
               # вернём текущую строку обратно в поток ввода,
               #  а прочитанный абзац отправим на вывод
               $self->unshiftline($line,$lref);

               # Now that the document is formed, translate it:
               #   - Уберём ведущий тег
               $paragraph =~ s/^<p>//s;

               #   - Отправим на вывод этот тег (непереведённым)
               #     и сам абзац (переведённый)
               $self->pushline(  "<p>"
                               . $self->translate($paragraph,$pararef)
                               );

               next PARAGRAPH;
           } else {
               # Присоединить в конец текущего абзаца
               $paragraph .= $line;
               $pararef = $lref unless(length($pararef));
           }

           # Переинициализировать цикл
           ($line,$lref)=$self->shiftline();
       }
       # Не получили строку? Значит это конец файла.
       return;
   }
 }

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

ПУБЛИЧНЫЙ ИНТЕРФЕЙС для скриптов, использующих ваш парсер

  Constructor

process(%)
    Эта функция может выполнить все, что вам нужно, с документом po4a за один вызов. Ее аргументы должны быть упакованы в виде хэша. ДЕЙСТВИЯ:

    a.  Читает все PO-файлы, указанные в po_in_name

    b.  Читает все исходные документы, указанные в file_in_name

    c.  Выполняет разбор документа

    d.  Читает и применяет все указанные дополнения

    e.  Записывает переведенный документ в file_out_name (если указано)

    f.  Записывает извлеченный PO-файл в po_out_name (если указано)

    АРГУМЕНТЫ, кроме тех, которые принимаются new() (с ожидаемым типом):

    file_in_name (@)
        Список имен файлов входных документов.

    file_in_charset ($)
        Кодировка, используемая во входном документе (если не указано, используется UTF-8).

    file_out_name ($)
        Имя файла выходного документа.

    file_out_charset ($)
        Кодировка, используемая в выходном документе (если не указано, используется UTF-8).

    po_in_name (@)
        Список имен входных PO-файлов, которые будут использоваться для перевода документа.

    po_out_name ($)
        Имя выходного PO-файла, содержащего строки, извлеченные из входного документа.

    addendum (@)
        Список имен файлов аддендумов.

    addendum_charset ($)
        Кодировка аддендума.

new(%)
    Создать новый po4a-документ. Допустимые параметры (в хэше, передаваемом в качестве параметра):

    verbose ($)
        Устанавливает уровень детализации.

    debug ($)
        Устанавливает режим отладки.

    wrapcol ($)
        Количество символов в строке, после которых текст в выходном документе переносится на новую строку (значение по умолчанию: 76).

        Отрицательное значение означает, что строки вообще не переносятся.

    Также для базовых PO-файлов принимаются следующие аргументы: porefs, copyright-holder, msgid-bugs-address, package-name, package-version, wrap-po.

  Манипулирование файлами документов

read($$$)
    Добавить содержимое другого входного документа в конец существующего массива "@{$self->{TT}{doc_in}}".

    Эта функция принимает два обязательных аргумента и один необязательный. * Имя файла, считываемого с диска; * Имя используемое в качестве имени файла при создании ссылки в PO-файле; * Кодировка, используемая при чтении файла (по умолчанию UTF-8)

    Массив "@{$self->{TT}{doc_in}}" содержит данные входного документа в виде массива строк с чередующимися значениями.
     * Строка $textline содержит каждую строку входных текстовых данных.
     * Строка "$filename:$linenum" содержит ее местоположение и называется
       "ссылкой" ("linenum" начинается с 1).

    Обратите внимание, что эта функция ничего не анализирует. Вам следует использовать функцию parse(), когда вы закончите упаковывать входные файлы в документ.

write($)
    Записывает переведенный документ в указанное имя файла.

    Эти переведенные данные предоставляются с помощью:
     * "$self->docheader()" содержит текст заголовка для плагина, и
     * "@{$self->{TT}{doc_out}}" содержит в массиве каждую строку главного переведенного текста.

  Манипулирование PO-файлами

readpo($)
    Добавляет содержимое файла (имя которого передано в качестве аргумента) в существующий входной PO-файл. Старое содержимое не удаляется.

writepo($)
    Записывает извлеченный PO-файл в файл в указанным именем.

stats()
    Возвращает некоторую статистику о выполненном на данный момент переводе. Обратите внимание, что это не та статистика, которая выводится командой msgfmt --statistic.Здесь приведена статистика о недавнем использовании файла PO, в то время как msgfmt сообщает о состоянии файла. Это обертка для функции Locale::Po4a::Po::stats_get, применяемой к входному PO-файлу. Пример использования:

        ($percent,$hit,$queries) = $document->stats();
        print "We found translations for $percent\%  ($hit from $queries) of strings.\n";

  Манипулирование аддендумами

addendum($)
    Пожалуйста, обратитесь к po4a(7) для получения более подробной информации о том, что такое аддендумы и как переводчикам следует их писать. Чтобы применить аддендум к переведенному документу, просто передайте его имя в эту функцию, и все готово ;)

    В случае ошибки эта функция возвращает числовое значение, отличное от нуля.

ВНУТРЕННИЕ ФУНКЦИИ, используемые при создании производных парсеров

  Получение входных данных, предоставление выходных данных
Для получения входных данных и возврата выходных данных предусмотрены четыре функции. Они очень похожи на shift/unshift и push/pop в Perl.

 * Оператор shift в Perl удаляет из массива первый элемент и возвращает его значение.
 * Оператор unshift в Perl добавляет элемент в начало массива.
 * Оператор pop в Perl удаляет из массива последний элемент и возвращает его значение.
 * Оператор push в Perl добавляет элемент в конец массива.

Первая пара относится к вводу, а вторая -- к выводу. Мнемоника: при вводе вас интересует первая строка -- то, что дает shift, а при выводе вы хотите добавить ваш результат в конец, как это делает push.

shiftline()
    Эта функция возвращает первую строку для анализа и соответствующую ссылку (упакованную в виде массива) из массива "@{$self->{TT}{doc_in}}" и удаляет эти первые 2 элемента массива. Здесь ссылка представлена строкой "$filename:$linenum".

unshiftline($$)
    Отменяет последнее действие shiftline(). Строка и соотвествующая ссылка помещаются обратно в начало массива "{$self->{TT}{doc_in}}".

pushline($)
    Помещает новую строку в конец массива "{$self->{TT}{doc_out}}".

popline()
    Удаляет из массива "{$self->{TT}{doc_out}}" последний элемент и возвращает его значение.

  Пометка строк как переводимых
Предусмотрена одна функция для обработки текста, который должен быть переведен.

translate($$$)
    Обязательные агрументы:

    -   Строка для перевода

    -   Ссылка на эту строку (т.е. её позиция во входном файле)

    -   Тип строки (т.е. текстовое описание её структурной роли; используется в Locale::Po4a::Po::gettextization(); см. также po4a(7), раздел Как это работает?)

    Эта функция также может принимать некоторые дополнительные аргументы. Они должны быть организованы в виде хэша. Например:

      $self->translate("string","ref","type",
                       'wrap' => 1);

    wrap
        логическое значение, указывающее, можем ли мы считать, что пробелы в строке не важны. Если да, функция канонизирует строку перед поиском перевода или его извлечением и добавляет переносы строк в результат по необходимости (вызывая wrap()).

    wrapcol
        количество символов в строке, после которых должен выполняться автоматический перенос текста на новую строку (по умолчанию: значение wrapcol, указанное при создании TransTractor или 76).

        Отрицательное значение будет вычитаться из значения по умолчанию.

    comment
        дополнительный комментарий для добавления к записи.

    Действия:

    -   Отправляет строку, ссылку и тип в po_out.

    -   Возвращает перевод строки (найденный в po_in), чтобы парсер мог создать doc_out.

    -   Обрабатывает кодировки для перекодирования строк перед отправкой их в po_out и перед возвратом переводов.

  Прочие функции

verbose()
    Возвращает значение 1, если параметр verbose был передан во время создания TransTractor.

debug()
    Возвращает значение 1, если параметр debug был передан во время создания TransTractor.

get_in_charset()
    Эта функция возвращает кодировку, которая была предоставлена в качестве основной

get_out_charset()
    Эта функция вернет кодировку, которая должна использоваться в выходном документе (обычно это полезно для замены обнаруженной кодировки входного документа там, где она была найдена).

    Эта функция будет использовать выходную кодировку, указанную в командной строке. Если кодировка не была указана, будет использоваться кодировка, указанная во входном PO-файле, если же во входном PO-файле кодировка имеет значение "CHARSET", функция вернет кодировку входного документа, в этом случае перекодирование выполнено не будет.

ПЛАНЫ НА БУДУЩЕЕ
Одним из недостатков текущего TransTractor является то, что он не может обрабатывать переведенные документы, содержащие все языки, такие как шаблоны debconf или файлы .desktop.

Для решения этой проблемы необходимы только следующие изменения интерфейса:

-   взять хэш в виде po_in_name (список для каждого языка)

-   добавить аргумент перевода для указания целевого языка

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

        $self->pushline_all({ "Description[".$langcode."]=".
                              $self->translate($line,$ref,$langcode)
                            });

Посмотрим, достаточно ли этого ;)

АВТОРЫ
 Денис Барбье (Denis Barbier) <barbier@linuxfr.org>
 Мартин Кенсон (Martin Quinson) (mquinson#debian.org)
 Жорди Вилальта (Jordi Vilalta) <jvprat@gmail.com>