=encoding utf-8

=head1 NAME

App::Greple::xlate - greple 的翻译支持模块

=head1 SYNOPSIS

    greple -Mxlate::deepl --xlate pattern target-file

    greple -Mxlate::gpt5 --xlate pattern target-file

    greple -Mxlate --xlate-engine gpt5 --xlate pattern target-file

=head1 VERSION

Version 1.0201

=head1 DESCRIPTION

B<Greple> B<xlate> 模块查找所需的文本块，并用翻译后的文本替换它们。目前 DeepL（F<deepl.pm>）和 GPT-5.5（F<gpt5.pm>）模块已作为后端引擎实现。

如果你想翻译以 Perl 的 pod 风格编写的文档中的普通文本块，可像这样将 B<greple> 命令与 C<xlate::deepl> 和 C<perl> 模块一起使用：

    greple -Mxlate::deepl -Mperl --pod --re '^([\w\pP].*\n)+' --all foo.pm

在此命令中，模式字符串 C<^([\w\pP].*\n)+> 表示以字母数字和标点符号开头的连续行。该命令会高亮显示将被翻译的区域。选项 B<--all> 用于生成完整文本。

=for html <p>
<img width="750" src="https://raw.githubusercontent.com/kaz-utashiro/App-Greple-xlate/main/images/select-area.png">
</p>

然后添加 C<--xlate> 选项以翻译所选区域。接着，它会找到所需的部分并将其替换为 B<deepl> 命令的输出。

默认情况下，原文与译文会以与 L<git(1)> 兼容的“冲突标记”格式打印。使用 C<ifdef> 格式，你可以轻松通过 L<unifdef(1)> 命令获取所需部分。输出格式可由 B<--xlate-format> 选项指定。

=for html <p>
<img width="750" src="https://raw.githubusercontent.com/kaz-utashiro/App-Greple-xlate/main/images/format-conflict.png">
</p>

如果你想翻译整篇文本，使用 B<--match-all> 选项。这是指定匹配整篇文本的模式 C<(?s).+> 的快捷方式。

冲突标记格式的数据可用 L<sdif|App::sdif> 命令配合 C<-V> 选项以并列样式查看。由于逐字符串比较没有意义，建议使用 C<--no-cdif> 选项。如果不需要为文本着色，指定 C<--no-textcolor>（或 C<--no-tc>）。

    sdif -V --no-filename --no-tc --no-cdif data_shishin.deepl-EN-US.cm

=for html <p>
<img width="750" src="https://raw.githubusercontent.com/kaz-utashiro/App-Greple-xlate/main/images/sdif-cm-view.png">
</p>

=head1 NORMALIZATION

处理按指定的单位进行，但对于多行非空文本的序列，会合并为一行。该操作按如下方式执行：

=over 2

=item *

移除每行开头和结尾的空白。

=item *

如果一行以全角标点符号结尾，则与下一行连接。

=item *

如果一行以全角字符结尾且下一行以全角字符开头，则连接这些行。

=item *

如果行的结尾或开头不是全角字符，则在中间插入一个空格字符后再连接。

=back

缓存数据基于规范化后的文本进行管理，因此即使进行了不影响规范化结果的修改，缓存的翻译数据仍然有效。

该规范化过程仅对第一个（第 0 个）以及偶数序号的模式执行。因此，如果指定了如下两个模式，则与第一个模式匹配的文本会在规范化后处理，与第二个模式匹配的文本则不执行规范化。

    greple -Mxlate -E normalized -E not-normalized

因此，对于需要将多行合并为一行来处理的文本，使用第一个模式；对于预格式化文本，使用第二个模式。如果第一个模式中没有可匹配的文本，则使用诸如 C<(?!)> 之类不匹配任何内容的模式。

=head1 MASKING

有时会有不想被翻译的文本部分。例如，Markdown 文件中的标签。DeepL 建议在这种情况下，将需要排除的文本部分转换为 XML 标签，进行翻译后再恢复。为支持此流程，可以指定从翻译中屏蔽的部分。

    --xlate-setopt maskfile=MASKPATTERN

这将把文件的每一行 C<MASKPATTERN> 解释为正则表达式，翻译与其匹配的字符串，并在处理后还原。以 C<#> 开头的行将被忽略。

复杂的模式可以使用反斜杠转义换行写在多行上。

通过 B<--xlate-mask> 选项可以查看文本经屏蔽转换后的样子。

此接口为实验性质，将来可能会改变。

=head1 OPTIONS

=over 7

=item B<--xlate>

=item B<--xlate-color>

=item B<--xlate-fold>

=item B<--xlate-fold-width>=I<n> (Default: 70)

对每个匹配区域启动翻译过程。

没有该选项时，B<greple> 的行为与普通搜索命令相同。因此，你可以在实际执行之前确认文件的哪些部分将成为翻译对象。

命令结果输出到标准输出，如有需要可重定向到文件，或考虑使用 L<App::Greple::update> 模块。

选项 B<--xlate> 会以 B<--color=never> 选项调用 B<--xlate-color> 选项。

使用 B<--xlate-fold> 选项时，转换后的文本按指定宽度折行。默认宽度为 70，可由 B<--xlate-fold-width> 选项设置。为行内操作预留四列，因此每行最多可容纳 74 个字符。

=item B<--xlate-engine>=I<engine>

指定要使用的翻译引擎。如果直接指定引擎模块，例如 C<-Mxlate::deepl>，则无需使用此选项。

目前可用的引擎如下所示

=over 2

=item * B<deepl>: DeepL API

=item * B<gpt3>: gpt-3.5-turbo

=item * B<gpt4o>: gpt-4o-mini

B<gpt-4o> 的接口不稳定，目前无法保证能正确工作。

=item * B<gpt5>: gpt-5.5

=back

=item B<--xlate-labor>

=item B<--xlabor>

不调用翻译引擎，而是期望由你手动完成。准备好要翻译的文本后，它们会被复制到剪贴板。你需要将其粘贴到表单中，将结果复制回剪贴板并按回车。

=item B<--xlate-to> (Default: C<EN-US>)

指定目标语言。使用 B<DeepL> 引擎时，可以通过 C<deepl languages> 命令获取可用语言。

=item B<--xlate-format>=I<format> (Default: C<conflict>)

指定原文与译文的输出格式。

除 C<xtxt> 外，以下格式均假定要翻译的部分是由多行构成。实际上也可以只翻译一行中的一部分，但指定为非 C<xtxt> 的格式将不会产生有意义的结果。

=over 4

=item B<conflict>, B<cm>

原文与转换后的文本以 L<git(1)> 冲突标记格式输出。

    <<<<<<< ORIGINAL
    original text
    =======
    translated Japanese text
    >>>>>>> JA

可以通过下一条 L<sed(1)> 命令恢复原始文件。

    sed -e '/^<<<<<<< /d' -e '/^=======$/,/^>>>>>>> /d'

=item B<colon>, I<:::::::>

原文与译文以 Markdown 的自定义容器样式输出。

    ::::::: ORIGINAL
    original text
    :::::::
    ::::::: JA
    translated Japanese text
    :::::::

上述文本在 HTML 中会被转换为如下形式。

    <div class="ORIGINAL">
    original text
    </div>
    <div class="JA">
    translated Japanese text
    </div>

冒号数量默认是 7。如果指定了类似 C<:::::> 的冒号序列，则使用该序列替代 7 个冒号。

=item B<ifdef>

原文与转换后的文本以 L<cpp(1)> C<#ifdef> 格式输出。

    #ifdef ORIGINAL
    original text
    #endif
    #ifdef JA
    translated Japanese text
    #endif

你可以通过 B<unifdef> 命令仅提取日文文本：

    unifdef -UORIGINAL -DJA foo.ja.pm

=item B<space>

=item B<space+>

原文和转换后的文本以一个空行分隔打印。对于C<space+>，还会在转换后的文本后输出一个换行符。

=item B<xtxt>

如果格式是C<xtxt>（译文）或未知，则只打印译文。

=back

=item B<--xlate-maxlen>=I<chars> (Default: 0)

指定一次发送到 API 的文本最大长度。默认值按 DeepL 免费账户服务设置：API 为 128K（B<--xlate>），剪贴板接口为 5000（B<--xlate-labor>）。如果使用 Pro 服务，您可以更改这些值。

=item B<--xlate-maxline>=I<n> (Default: 0)

指定一次发送到 API 的文本最大行数。

如果希望逐行翻译，将此值设为 1。此选项优先于C<--xlate-maxlen>选项。

=item B<--xlate-prompt>=I<text>

指定要发送给翻译引擎的自定义提示。此选项仅在使用 ChatGPT 引擎（gpt3、gpt4o、gpt5）时可用。你可以通过向 AI 模型提供特定指令来自定义翻译行为。如果提示包含 C<%s>，它将被替换为目标语言名称。

=item B<--xlate-context>=I<text>

指定要发送给翻译引擎的附加上下文信息。此选项可多次使用以提供多个上下文字符串。上下文信息有助于翻译引擎理解背景并生成更准确的译文。

=item B<--xlate-glossary>=I<glossary>

指定用于翻译的术语库（glossary）ID。此选项仅在使用 DeepL 引擎时可用。术语库 ID 应从您的 DeepL 账户获取，以确保特定术语的一致翻译。

=item B<-->[B<no->]B<xlate-progress> (Default: True)

在 STDERR 输出中实时查看翻译结果。

=item B<--xlate-stripe>

使用L<App::Greple::stripe>模块以斑马条纹方式显示匹配部分。当匹配部分首尾相接时很有用。

颜色调色板会根据终端的背景颜色切换。若要显式指定，可使用B<--xlate-stripe-light>或B<--xlate-stripe-dark>。

=item B<--xlate-mask>

执行遮罩功能，并按转换后文本原样显示而不进行还原。

=item B<--match-all>

将整个文件的文本设为目标区域。

=item B<--lineify-cm>

=item B<--lineify-colon>

在C<cm>和C<colon>格式的情况下，输出会按行拆分并格式化。因此，如果仅需要翻译某行的一部分，将无法获得预期结果。这些过滤器会将因部分行翻译而破坏的输出修复为正常的逐行输出。

在当前实现中，如果一行中的多个部分被翻译，它们会作为独立的行输出。

=back

=head1 CACHE OPTIONS

B<xlate>模块可为每个文件存储翻译的缓存文本，并在执行前读取，以消除向服务器请求的开销。使用默认的缓存策略C<auto>时，仅当目标文件存在缓存文件时才维护缓存数据。

使用B<--xlate-cache=clear>启动缓存管理或清理所有现有缓存数据。使用此选项执行后，如果不存在缓存文件将创建新的缓存文件，并在此后自动维护。

=over 7

=item --xlate-cache=I<strategy>

=over 4

=item C<auto> (Default)

如果缓存文件存在则进行维护。

=item C<create>

创建空的缓存文件并退出。

=item C<always>, C<yes>, C<1>

只要目标是普通文件，无论如何都维护缓存。

=item C<clear>

先清除缓存数据。

=item C<never>, C<no>, C<0>

即使存在，也绝不使用缓存文件。

=item C<accumulate>

在默认行为下，未使用的数据会从缓存文件中移除。如果你不想删除它们并希望保留在文件中，请使用C<accumulate>。

=back

=item B<--xlate-update>

此选项会强制更新缓存文件，即使没有必要。

=back

=head1 COMMAND LINE INTERFACE

你可以通过使用发行版中包含的C<xlate>命令，从命令行轻松使用该模块。用法请参见C<xlate>手册页。

命令 C<xlate> 支持 GNU 风格的长选项，例如 C<--to-lang>、C<--from-lang>、C<--engine> 和 C<--file>。使用 C<xlate -h> 查看所有可用选项。

C<xlate>命令与 Docker 环境协同工作，因此即使本地未安装任何东西，只要有 Docker 可用就能使用。请使用C<-D>或C<-C>选项。

Docker 操作由L<App::dozo>处理，它也可以作为独立命令使用。C<dozo>命令支持C<.dozorc>配置文件以持久化容器设置。

此外，由于提供了适用于各种文档样式的 makefile，无需特殊指定即可翻译成其他语言。请使用C<-M>选项。

你也可以将 Docker 和C<make>选项组合使用，以便在 Docker 环境中运行C<make>。

像C<xlate -C>这样运行会启动一个挂载了当前工作 git 仓库的 shell。

请阅读L</SEE ALSO>章节中的日文文章以了解详情。

=head1 EMACS

加载仓库中包含的F<xlate.el>文件，以便在 Emacs 编辑器中使用C<xlate>命令。C<xlate-region>函数会翻译给定区域。默认语言是C<EN-US>，你可以通过带前缀参数调用来指定语言。

=for html <p>
<img width="750" src="https://raw.githubusercontent.com/kaz-utashiro/App-Greple-xlate/main/images/emacs.png">
</p>

=head1 ENVIRONMENT

=over 7

=item DEEPL_AUTH_KEY

设置你的 DeepL 服务认证密钥。

=item OPENAI_API_KEY

OpenAI 认证密钥。

=back

=head1 INSTALL

=head2 CPANMINUS

    $ cpanm App::Greple::xlate

=head2 TOOLS

你需要安装 DeepL 和 ChatGPT 的命令行工具。

L<https://github.com/DeepLcom/deepl-python>

L<https://github.com/tecolicom/App-gpty>

=head1 SEE ALSO

=head2 MODULES

L<App::Greple::xlate::deepl>, L<App::Greple::xlate::gpt5>

L<App::dozo> - 由 xlate 用于容器操作的通用 Docker 运行器

=head2 RELATED MODULES

=over 2

=item * L<App::Greple>

关于目标文本模式的详细信息请参见B<greple>手册。使用B<--inside>、B<--outside>、B<--include>、B<--exclude>选项限制匹配区域。

=item * L<App::Greple::update>

你可以使用C<-Mupdate>模块根据B<greple>命令的结果来修改文件。

=item * L<App::sdif>

使用B<sdif>与B<-V>选项并排显示冲突标记格式。

=item * L<App::Greple::stripe>

通过B<--xlate-stripe>选项使用 Greple B<stripe>模块。

=back

=head2 RESOURCES

=over 2

=item * L<https://hub.docker.com/r/tecolicom/xlate>

Docker 容器镜像。

=item * L<https://github.com/tecolicom/getoptlong>

用于在C<xlate>脚本和L<App::dozo>中进行选项解析的C<getoptlong.sh>库。

=item * L<https://github.com/DeepLcom/deepl-python>

DeepL Python 库和 CLI 命令。

=item * L<https://github.com/openai/openai-python>

OpenAI Python 库

=item * L<https://github.com/tecolicom/App-gpty>

OpenAI 命令行界面

=back

=head2 ARTICLES

=over 2

=item * L<https://qiita.com/kaz-utashiro/items/1c1a51a4591922e18250>

用于仅将必要部分通过 DeepL API 翻译并替换的 Greple 模块（日文）

=item * L<https://qiita.com/kaz-utashiro/items/a5e19736416ca183ecf6>

使用 DeepL API 模块生成 15 种语言的文档（日文）

=item * L<https://qiita.com/kaz-utashiro/items/1b9e155d6ae0620ab4dd>

使用 DeepL API 的自动翻译 Docker 环境（日文）

=back

=head1 AUTHOR

Kazumasa Utashiro

=head1 LICENSE

Copyright © 2023-2026 Kazumasa Utashiro.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
