=encoding utf-8

=head1 NAME

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

=head1 SYNOPSIS

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

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

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

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

=head1 VERSION

Version 0.9918

=head1 DESCRIPTION

B<Greple> B<xlate> 模块查找所需的文本块并用翻译后的文本替换它们。目前已实现 DeepL（F<deepl.pm>）、ChatGPT 4.1（F<gpt4.pm>）和 GPT-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

这会将文件 `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<--xlate-color> 选项并带有 B<--color=never> 选项。

使用 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<gpt4>: gpt-4.1

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

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

=item * B<gpt5>: gpt-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+>

原文和转换后的文本之间用一个空行分隔打印。

=item B<xtxt>

对于 C<space+>，在转换后的文本后面也会输出一个换行符。

=back

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

如果格式为 C<xtxt>（翻译后的文本）或未知，则只打印翻译后的文本。

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

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

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

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

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

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

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

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

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

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

如果您希望一次只翻译一行，请将此值设置为 1。此选项优先于 C<--xlate-maxlen> 选项。

=item B<--xlate-stripe>

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

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

=item B<--xlate-mask>

颜色调色板会根据终端的背景色切换。如果您想明确指定，可以使用 B<--xlate-stripe-light> 或 B<--xlate-stripe-dark>。

=item B<--match-all>

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

=item B<--lineify-cm>

=item B<--lineify-colon>

对于 C<cm> 和 C<colon> 格式，输出会被逐行拆分和格式化。因此，如果只翻译了一行的一部分，将无法获得预期的结果。这些过滤器可以修复因只翻译部分内容而导致输出损坏的问题，使其恢复为正常的逐行输出。

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

=back

=head1 CACHE OPTIONS

将文件的全部文本设置为目标区域。

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

=over 7

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

=over 4

=item C<auto> (Default)

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

=item C<create>

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

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

创建空缓存文件并退出。

=item C<clear>

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

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

首先清除缓存数据。

=item C<accumulate>

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

=back

=item B<--xlate-update>

默认情况下，未使用的数据会从缓存文件中移除。如果您不想移除并希望保留在文件中，请使用 C<accumulate>。

=back

=head1 COMMAND LINE INTERFACE

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

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

您可以通过分发包中包含的 C<xlate> 命令在命令行中轻松使用此模块。用法请参见 C<xlate> man 页面。

Docker 操作由 C<xrun> 脚本处理，该脚本也可以作为独立命令使用。C<xrun> 脚本支持 C<.xrunrc> 配置文件以实现容器设置的持久化。

C<xlate> 命令可与 Docker 环境协同工作，因此即使您手头没有安装任何东西，只要有 Docker 可用，也可以使用。请使用 C<-D> 或 C<-C> 选项。

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

您还可以结合 Docker 和 C<make> 选项，在 Docker 环境中运行 C<make>。

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

=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

L<App::Greple::xlate>

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

L<App::Greple::xlate::gpt4>

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

=over 2

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

Docker 容器镜像。

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

C<getoptlong.sh> 库用于 C<xlate> 和 C<xrun> 脚本中的选项解析。

=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 命令行界面

=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>

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

=back

=head2 ARTICLES

=over 2

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

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

=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-2025 Kazumasa Utashiro.

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

=cut
