gtbabel/core

即时PHP服务器端翻译。

1.2.0 2022-09-14 11:53 UTC

This package is auto-updated.

Last update: 2024-09-24 05:00:14 UTC


README

Build Status

🌐 Gtbabel 🌐

Gtbabel自动翻译您的HTML/PHP页面 - 服务器端。

基本思想

  • Gtbabel在每次页面加载时将任何页面提取为逻辑段落标记。
  • 静态和动态内容被有意地同等对待。
  • 所有标记在渲染前(如果可用)都替换为其翻译。
  • 标记被输出(如果不可用),在那里可以进行翻译。

功能

  • 轻量级:只有约3500行代码。
  • 框架无关:与几乎所有基于PHP的CMS或静态站点一起工作。
  • 快速:一旦所有翻译都可用,Gtbabel的吞吐量达到约4000个单词/秒。
  • 自动翻译:使用Google翻译APIMicrosoft翻译APIDeepL的强大功能,将您的页面和链接自动翻译成任何语言。
  • 自定义提供者:连接自定义翻译API
  • 包含路由器:欺骗请求URI并让魔法发生,链接会自动转换(带有slug冲突检测)。
  • 包含当前语言和所有可用语言的辅助函数。
  • 考虑基本的SEO:包括标题标签、SEO描述、open graph标签、html lang属性、hreflang标签。
  • 支持RTL:如果语言是RTL,添加html dir属性。
  • 提供WordPress插件。
  • 为翻译管理做好准备。
  • 与缓存/预加载插件无缝工作。
  • 除了纯HTML外,还处理并翻译xml(如动态生成的站点地图)和json(如ajax响应)。
  • 提供PHPUnit e2e测试。
  • 直接导出到gettext(使用提取的注释和模板文件)。
  • 同时支持多种源语言。
  • 提供停用词以防止对特定单词进行翻译。
  • 完全隐藏语言以提前准备新翻译。
  • DOM更改检测:观察特定页面部分并翻译动态内容。
  • 自动节流API翻译以控制成本。
  • 支持自定义域名(基于路径、基于子域、基于顶级域)。
  • 包含前端编辑器。

要求

  • PHP >=7.2

安装

使用composer安装一次

composer require gtbabel/core

然后将其添加到您的文件中

require __DIR__ . '/vendor/autoload.php';
use gtbabel\core\Gtbabel;

使用

$gtbabel = new Gtbabel();

$gtbabel->start();

// any static or dynamic content
require_once 'template.html';

$gtbabel->stop();

配置

如果您不提供初始配置,Gtbabel将使用合理的默认设置开始。
您还可以直接作为数组提供设置,或传递一个json文件的路径以覆盖(部分)默认设置

$gtbabel = new Gtbabel();

// set settings from php
$gtbabel->config(['languages' => [['code' => 'de', 'label' => 'Deutsch'], ['code' => 'en', 'label' => 'English']], ...]);

// set settings from json
$gtbabel->config('settings.json');

默认配置为

[
    'languages' => [
        [
            'code' => 'de',
            'label' => 'Deutsch',
            'rtl' => false,
            'hreflang_code' => 'de',
            'google_translation_code' => 'de',
            'microsoft_translation_code' => 'de',
            'deepl_translation_code' => 'de',
            'hidden' => false
        ],
        [
            'code' => 'en',
            'label' => 'English',
            'rtl' => false,
            'hreflang_code' => 'en',
            'google_translation_code' => 'en',
            'microsoft_translation_code' => 'en',
            'deepl_translation_code' => 'en',
            'hidden' => false
        ],
        [
            'code' => 'fr',
            'label' => 'Français',
            'rtl' => false,
            'hreflang_code' => 'fr',
            'google_translation_code' => 'fr',
            'microsoft_translation_code' => 'fr',
            'deepl_translation_code' => 'fr',
            'hidden' => false
        ],
        [
            'code' => 'af',
            'label' => 'Afrikaans',
            'rtl' => false,
            'hreflang_code' => 'af',
            'google_translation_code' => 'af',
            'microsoft_translation_code' => 'af',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'am',
            'label' => 'አማርኛ',
            'rtl' => false,
            'hreflang_code' => 'am',
            'google_translation_code' => 'am',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ar',
            'label' => 'العربية',
            'rtl' => true,
            'hreflang_code' => 'ar',
            'google_translation_code' => 'ar',
            'microsoft_translation_code' => 'ar',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'az',
            'label' => 'Azərbaycan',
            'rtl' => false,
            'hreflang_code' => 'az',
            'google_translation_code' => 'az',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'be',
            'label' => 'беларускі',
            'rtl' => false,
            'hreflang_code' => 'be',
            'google_translation_code' => 'be',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'bg',
            'label' => 'български',
            'rtl' => false,
            'hreflang_code' => 'bg',
            'google_translation_code' => 'bg',
            'microsoft_translation_code' => 'bg',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'bn',
            'label' => 'বাঙালির',
            'rtl' => false,
            'hreflang_code' => 'bn',
            'google_translation_code' => 'bn',
            'microsoft_translation_code' => 'bn',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'bs',
            'label' => 'Bosanski',
            'rtl' => false,
            'hreflang_code' => 'bs',
            'google_translation_code' => 'bs',
            'microsoft_translation_code' => 'bs',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ca',
            'label' => 'Català',
            'rtl' => false,
            'hreflang_code' => 'ca',
            'google_translation_code' => 'ca',
            'microsoft_translation_code' => 'ca',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ceb',
            'label' => 'Cebuano',
            'rtl' => false,
            'hreflang_code' => null,
            'google_translation_code' => 'ceb',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'co',
            'label' => 'Corsican',
            'rtl' => false,
            'hreflang_code' => 'co',
            'google_translation_code' => 'co',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'cs',
            'label' => 'Český',
            'rtl' => false,
            'hreflang_code' => 'cs',
            'google_translation_code' => 'cs',
            'microsoft_translation_code' => 'cs',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'cy',
            'label' => 'Cymraeg',
            'rtl' => false,
            'hreflang_code' => 'cy',
            'google_translation_code' => 'cy',
            'microsoft_translation_code' => 'cy',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'da',
            'label' => 'Dansk',
            'rtl' => false,
            'hreflang_code' => 'da',
            'google_translation_code' => 'da',
            'microsoft_translation_code' => 'da',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'el',
            'label' => 'ελληνικά',
            'rtl' => false,
            'hreflang_code' => 'el',
            'google_translation_code' => 'el',
            'microsoft_translation_code' => 'el',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'eo',
            'label' => 'Esperanto',
            'rtl' => false,
            'hreflang_code' => 'eo',
            'google_translation_code' => 'eo',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'es',
            'label' => 'Español',
            'rtl' => false,
            'hreflang_code' => 'es',
            'google_translation_code' => 'es',
            'microsoft_translation_code' => 'es',
            'deepl_translation_code' => 'es',
            'hidden' => false
        ],
        [
            'code' => 'et',
            'label' => 'Eesti',
            'rtl' => false,
            'hreflang_code' => 'et',
            'google_translation_code' => 'et',
            'microsoft_translation_code' => 'et',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'eu',
            'label' => 'Euskal',
            'rtl' => false,
            'hreflang_code' => 'eu',
            'google_translation_code' => 'eu',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'fa',
            'label' => 'فارسی',
            'rtl' => true,
            'hreflang_code' => 'fa',
            'google_translation_code' => 'fa',
            'microsoft_translation_code' => 'fa',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'fi',
            'label' => 'Suomalainen',
            'rtl' => false,
            'hreflang_code' => 'fi',
            'google_translation_code' => 'fi',
            'microsoft_translation_code' => 'fi',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ga',
            'label' => 'Gaeilge',
            'rtl' => false,
            'hreflang_code' => 'ga',
            'google_translation_code' => 'ga',
            'microsoft_translation_code' => 'ga',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'gd',
            'label' => 'Gàidhlig',
            'rtl' => false,
            'hreflang_code' => 'gd',
            'google_translation_code' => 'gd',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'gl',
            'label' => 'Galego',
            'rtl' => false,
            'hreflang_code' => 'gl',
            'google_translation_code' => 'gl',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'gu',
            'label' => 'ગુજરાતી',
            'rtl' => false,
            'hreflang_code' => 'gu',
            'google_translation_code' => 'gu',
            'microsoft_translation_code' => 'gu',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ha',
            'label' => 'Hausa',
            'rtl' => true,
            'hreflang_code' => 'ha',
            'google_translation_code' => 'ha',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'haw',
            'label' => 'Hawaiian',
            'rtl' => false,
            'hreflang_code' => null,
            'google_translation_code' => 'haw',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'he',
            'label' => 'עברי',
            'rtl' => true,
            'hreflang_code' => 'he',
            'google_translation_code' => 'he',
            'microsoft_translation_code' => 'he',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'hi',
            'label' => 'हिन्दी',
            'rtl' => false,
            'hreflang_code' => 'hi',
            'google_translation_code' => 'hi',
            'microsoft_translation_code' => 'hi',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'hmn',
            'label' => 'Hmong',
            'rtl' => false,
            'hreflang_code' => null,
            'google_translation_code' => 'hmn',
            'microsoft_translation_code' => 'mww',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'hr',
            'label' => 'Hrvatski',
            'rtl' => false,
            'hreflang_code' => 'hr',
            'google_translation_code' => 'hr',
            'microsoft_translation_code' => 'hr',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ht',
            'label' => 'Kreyòl',
            'rtl' => false,
            'hreflang_code' => 'ht',
            'google_translation_code' => 'ht',
            'microsoft_translation_code' => 'ht',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'hu',
            'label' => 'Magyar',
            'rtl' => false,
            'hreflang_code' => 'hu',
            'google_translation_code' => 'hu',
            'microsoft_translation_code' => 'hu',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'hy',
            'label' => 'հայերեն',
            'rtl' => false,
            'hreflang_code' => 'hy',
            'google_translation_code' => 'hy',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'id',
            'label' => 'Indonesia',
            'rtl' => false,
            'hreflang_code' => 'id',
            'google_translation_code' => 'id',
            'microsoft_translation_code' => 'id',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ig',
            'label' => 'Igbo',
            'rtl' => false,
            'hreflang_code' => 'ig',
            'google_translation_code' => 'ig',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'is',
            'label' => 'Icelandic',
            'rtl' => false,
            'hreflang_code' => 'is',
            'google_translation_code' => 'is',
            'microsoft_translation_code' => 'is',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'it',
            'label' => 'Italiano',
            'rtl' => false,
            'hreflang_code' => 'it',
            'google_translation_code' => 'it',
            'microsoft_translation_code' => 'it',
            'deepl_translation_code' => 'it',
            'hidden' => false
        ],
        [
            'code' => 'ja',
            'label' => '日本の',
            'rtl' => false,
            'hreflang_code' => 'ja',
            'google_translation_code' => 'ja',
            'microsoft_translation_code' => 'ja',
            'deepl_translation_code' => 'ja',
            'hidden' => false
        ],
        [
            'code' => 'jv',
            'label' => 'Jawa',
            'rtl' => false,
            'hreflang_code' => 'jv',
            'google_translation_code' => 'jv',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ka',
            'label' => 'ქართული',
            'rtl' => false,
            'hreflang_code' => 'ka',
            'google_translation_code' => 'ka',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'kk',
            'label' => 'Қазақ',
            'rtl' => false,
            'hreflang_code' => 'kk',
            'google_translation_code' => 'kk',
            'microsoft_translation_code' => 'kk',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'km',
            'label' => 'ខ្មែរ',
            'rtl' => false,
            'hreflang_code' => 'km',
            'google_translation_code' => 'km',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'kn',
            'label' => 'ಕನ್ನಡ',
            'rtl' => false,
            'hreflang_code' => 'kn',
            'google_translation_code' => 'kn',
            'microsoft_translation_code' => 'kn',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ko',
            'label' => '한국의',
            'rtl' => false,
            'hreflang_code' => 'ko',
            'google_translation_code' => 'ko',
            'microsoft_translation_code' => 'ko',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ku',
            'label' => 'Kurdî',
            'rtl' => true,
            'hreflang_code' => 'ku',
            'google_translation_code' => 'ku',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ky',
            'label' => 'Кыргыз',
            'rtl' => false,
            'hreflang_code' => 'ky',
            'google_translation_code' => 'ky',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'la',
            'label' => 'Latine',
            'rtl' => false,
            'hreflang_code' => 'la',
            'google_translation_code' => 'la',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'lb',
            'label' => 'Lëtzebuergesch',
            'rtl' => false,
            'hreflang_code' => 'lb',
            'google_translation_code' => 'lb',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'lo',
            'label' => 'ລາວ',
            'rtl' => false,
            'hreflang_code' => 'lo',
            'google_translation_code' => 'lo',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'lt',
            'label' => 'Lietuvos',
            'rtl' => false,
            'hreflang_code' => 'lt',
            'google_translation_code' => 'lt',
            'microsoft_translation_code' => 'lt',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'lv',
            'label' => 'Latvijas',
            'rtl' => false,
            'hreflang_code' => 'lv',
            'google_translation_code' => 'lv',
            'microsoft_translation_code' => 'lv',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'mg',
            'label' => 'Malagasy',
            'rtl' => false,
            'hreflang_code' => 'mg',
            'google_translation_code' => 'mg',
            'microsoft_translation_code' => 'mg',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'mi',
            'label' => 'Maori',
            'rtl' => false,
            'hreflang_code' => 'mi',
            'google_translation_code' => 'mi',
            'microsoft_translation_code' => 'mi',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'mk',
            'label' => 'македонски',
            'rtl' => false,
            'hreflang_code' => 'mk',
            'google_translation_code' => 'mk',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ml',
            'label' => 'മലയാളം',
            'rtl' => false,
            'hreflang_code' => 'ml',
            'google_translation_code' => 'ml',
            'microsoft_translation_code' => 'ml',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'mn',
            'label' => 'Монгол',
            'rtl' => false,
            'hreflang_code' => 'mn',
            'google_translation_code' => 'mn',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'mr',
            'label' => 'मराठी',
            'rtl' => false,
            'hreflang_code' => 'mr',
            'google_translation_code' => 'mr',
            'microsoft_translation_code' => 'mr',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ms',
            'label' => 'Malay',
            'rtl' => false,
            'hreflang_code' => 'ms',
            'google_translation_code' => 'ms',
            'microsoft_translation_code' => 'ms',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'mt',
            'label' => 'Malti',
            'rtl' => false,
            'hreflang_code' => 'mt',
            'google_translation_code' => 'mt',
            'microsoft_translation_code' => 'mt',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'my',
            'label' => 'မြန်မာ',
            'rtl' => false,
            'hreflang_code' => 'my',
            'google_translation_code' => 'my',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ne',
            'label' => 'नेपाली',
            'rtl' => false,
            'hreflang_code' => 'ne',
            'google_translation_code' => 'ne',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'nl',
            'label' => 'Nederlands',
            'rtl' => false,
            'hreflang_code' => 'nl',
            'google_translation_code' => 'nl',
            'microsoft_translation_code' => 'nl',
            'deepl_translation_code' => 'nl',
            'hidden' => false
        ],
        [
            'code' => 'no',
            'label' => 'Norsk',
            'rtl' => false,
            'hreflang_code' => 'no',
            'google_translation_code' => 'no',
            'microsoft_translation_code' => 'nb',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ny',
            'label' => 'Nyanja',
            'rtl' => false,
            'hreflang_code' => 'ny',
            'google_translation_code' => 'ny',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'pa',
            'label' => 'ਪੰਜਾਬੀ',
            'rtl' => false,
            'hreflang_code' => 'pa',
            'google_translation_code' => 'pa',
            'microsoft_translation_code' => 'pa',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'pl',
            'label' => 'Polski',
            'rtl' => false,
            'hreflang_code' => 'pl',
            'google_translation_code' => 'pl',
            'microsoft_translation_code' => 'pl',
            'deepl_translation_code' => 'pl',
            'hidden' => false
        ],
        [
            'code' => 'ps',
            'label' => 'پښتو',
            'rtl' => true,
            'hreflang_code' => 'ps',
            'google_translation_code' => 'ps',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'pt-br',
            'label' => 'Português (Brasil)',
            'rtl' => false,
            'hreflang_code' => 'pt',
            'google_translation_code' => 'pt',
            'microsoft_translation_code' => 'pt-br',
            'deepl_translation_code' => 'pt',
            'hidden' => false
        ],
        [
            'code' => 'pt-pt',
            'label' => 'Português (Portugal)',
            'rtl' => false,
            'hreflang_code' => 'pt',
            'google_translation_code' => 'pt',
            'microsoft_translation_code' => 'pt-pt',
            'deepl_translation_code' => 'pt',
            'hidden' => false
        ],
        [
            'code' => 'ro',
            'label' => 'Românesc',
            'rtl' => false,
            'hreflang_code' => 'ro',
            'google_translation_code' => 'ro',
            'microsoft_translation_code' => 'ro',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ru',
            'label' => 'Русский',
            'rtl' => false,
            'hreflang_code' => 'ru',
            'google_translation_code' => 'ru',
            'microsoft_translation_code' => 'ru',
            'deepl_translation_code' => 'ru',
            'hidden' => false
        ],
        [
            'code' => 'sd',
            'label' => 'سنڌي',
            'rtl' => false,
            'hreflang_code' => 'sd',
            'google_translation_code' => 'sd',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'si',
            'label' => 'සිංහලයන්',
            'rtl' => false,
            'hreflang_code' => 'si',
            'google_translation_code' => 'si',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sk',
            'label' => 'Slovenský',
            'rtl' => false,
            'hreflang_code' => 'sk',
            'google_translation_code' => 'sk',
            'microsoft_translation_code' => 'sk',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sl',
            'label' => 'Slovenski',
            'rtl' => false,
            'hreflang_code' => 'sl',
            'google_translation_code' => 'sl',
            'microsoft_translation_code' => 'sl',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sm',
            'label' => 'Samoa',
            'rtl' => false,
            'hreflang_code' => 'sm',
            'google_translation_code' => 'sm',
            'microsoft_translation_code' => 'sm',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sn',
            'label' => 'Shona',
            'rtl' => false,
            'hreflang_code' => 'sn',
            'google_translation_code' => 'sn',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'so',
            'label' => 'Soomaali',
            'rtl' => false,
            'hreflang_code' => 'so',
            'google_translation_code' => 'so',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sq',
            'label' => 'Shqiptar',
            'rtl' => false,
            'hreflang_code' => 'sq',
            'google_translation_code' => 'sq',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sr-cy',
            'label' => 'Српски (ћирилица)',
            'rtl' => false,
            'hreflang_code' => 'sr',
            'google_translation_code' => 'sr',
            'microsoft_translation_code' => 'sr-Cyrl',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sr-la',
            'label' => 'Српски (латински)',
            'rtl' => false,
            'hreflang_code' => 'sr',
            'google_translation_code' => 'sr',
            'microsoft_translation_code' => 'sr-Latn',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'su',
            'label' => 'Sunda',
            'rtl' => false,
            'hreflang_code' => 'su',
            'google_translation_code' => 'su',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'sv',
            'label' => 'Svenska',
            'rtl' => false,
            'hreflang_code' => 'sv',
            'google_translation_code' => 'sv',
            'microsoft_translation_code' => 'sv',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ta',
            'label' => 'தமிழ்',
            'rtl' => false,
            'hreflang_code' => 'ta',
            'google_translation_code' => 'ta',
            'microsoft_translation_code' => 'ta',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'te',
            'label' => 'Telugu',
            'rtl' => false,
            'hreflang_code' => 'te',
            'google_translation_code' => 'te',
            'microsoft_translation_code' => 'te',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'tg',
            'label' => 'Тоҷикистон',
            'rtl' => false,
            'hreflang_code' => 'tg',
            'google_translation_code' => 'tg',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'th',
            'label' => 'ไทย',
            'rtl' => false,
            'hreflang_code' => 'th',
            'google_translation_code' => 'th',
            'microsoft_translation_code' => 'th',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'tr',
            'label' => 'Türk',
            'rtl' => false,
            'hreflang_code' => 'tr',
            'google_translation_code' => 'tr',
            'microsoft_translation_code' => 'tr',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'uk',
            'label' => 'Український',
            'rtl' => false,
            'hreflang_code' => 'uk',
            'google_translation_code' => 'uk',
            'microsoft_translation_code' => 'uk',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'ur',
            'label' => 'اردو',
            'rtl' => true,
            'hreflang_code' => 'ur',
            'google_translation_code' => 'ur',
            'microsoft_translation_code' => 'ur',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'uz',
            'label' => 'O\'zbekiston',
            'rtl' => false,
            'hreflang_code' => 'uz',
            'google_translation_code' => 'uz',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'vi',
            'label' => 'Tiếng việt',
            'rtl' => false,
            'hreflang_code' => 'vi',
            'google_translation_code' => 'vi',
            'microsoft_translation_code' => 'vi',
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'xh',
            'label' => 'IsiXhosa',
            'rtl' => false,
            'hreflang_code' => 'xh',
            'google_translation_code' => 'xh',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'yi',
            'label' => 'ייִדיש',
            'rtl' => true,
            'hreflang_code' => 'yi',
            'google_translation_code' => 'yi',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'yo',
            'label' => 'Yoruba',
            'rtl' => false,
            'hreflang_code' => 'yo',
            'google_translation_code' => 'yo',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ],
        [
            'code' => 'zh-cn',
            'label' => '中文(简体)',
            'rtl' => false,
            'hreflang_code' => 'zh-cn',
            'google_translation_code' => 'zh-cn',
            'microsoft_translation_code' => 'zh-Hans',
            'deepl_translation_code' => 'zh',
            'hidden' => false
        ],
        [
            'code' => 'zh-tw',
            'label' => '中文(繁體)',
            'rtl' => false,
            'hreflang_code' => 'zh-tw',
            'google_translation_code' => 'zh-tw',
            'microsoft_translation_code' => 'zh-Hant',
            'deepl_translation_code' => 'zh',
            'hidden' => false
        ],
        [
            'code' => 'zu',
            'label' => 'Zulu',
            'rtl' => false,
            'hreflang_code' => 'zu',
            'google_translation_code' => 'zu',
            'microsoft_translation_code' => null,
            'deepl_translation_code' => null,
            'hidden' => false
        ]
    ],
    'lng_source' => 'de',
    'lng_target' => null, // this gets automatically determined by the current url; you can set this manually (e.g. in a non-host based environment)
    'database' => [
        'type' => 'sqlite',
        'filename' => 'data.db',
        'table' => 'translations'
        // mysql/pgsql is also supported
        /*
        'type' => 'mysql',
        'host' => '127.0.0.1',
        'username' => 'xxxxxx',
        'password' => 'xxxxxx',
        'database' => 'xxxxxx',
        'port' => 3306,
        'table' => 'translations'
        */
    ],
    'log_folder' => '/logs',
    'redirect_root_domain' => 'browser', // browser|source|ip
    'basic_auth' => null, // provide basic auth (in the format "username:password") if you use Gtbabel in a password protected environment
    'translate_html' => true,
    'translate_html_include' => [
        [
            'selector' => '/html/body//text()',
            'attribute' => null,
            'context' => null,
            'comment' => 'Text nodes'
        ],
        [
            'selector' => '/html/body//a[starts-with(@href, \'mailto:\')]',
            'attribute' => 'href',
            'context' => 'email',
            'comment' => 'Email links'
        ],
        [
            'selector' => '/html/body//a[@href]',
            'attribute' => 'href',
            'context' => 'slug|file|url',
            'comment' => 'Links'
        ],
        [
            'selector' => '/html/body//form[@action]',
            'attribute' => 'action',
            'context' => 'slug|file|url',
            'comment' => 'Form actions'
        ],
        [
            'selector' => '/html/body//iframe[@src]',
            'attribute' => 'src',
            'context' => 'slug|file|url',
            'comment' => 'Iframe content'
        ],
        [
            'selector' => '/html/body//img[@alt]',
            'attribute' => 'alt',
            'context' => null,
            'comment' => 'Alt tags'
        ],
        [
            'selector' => '/html/body//*[@title]',
            'attribute' => 'title',
            'context' => null,
            'comment' => 'Title attributes'
        ],
        [
            'selector' => '/html/body//*[@placeholder]',
            'attribute' => 'placeholder',
            'context' => null,
            'comment' => 'Input placeholders'
        ],
        [
            'selector' => '/html/body//input[@type="submit"][@value]',
            'attribute' => 'value',
            'context' => null,
            'comment' => 'Submit values'
        ],
        [
            'selector' => '/html/body//input[@type="reset"][@value]',
            'attribute' => 'value',
            'context' => null,
            'comment' => 'Reset values'
        ],
        [
            'selector' => '/html/head//title',
            'attribute' => null,
            'context' => 'title',
            'comment' => 'Page title'
        ],
        [
            'selector' => '/html/head//meta[@name="description"][@content]',
            'attribute' => 'content',
            'context' => 'description',
            'comment' => 'Page description'
        ],
        [
            'selector' => '/html/head//link[@rel="canonical"][@href]',
            'attribute' => 'href',
            'context' => 'slug',
            'comment' => 'Canonical tags'
        ],
        [
            'selector' => '/html/head//meta[@property="og:title"][@content]',
            'attribute' => 'content',
            'context' => 'title',
            'comment' => 'Open Graph Tag'
        ],
        [
            'selector' => '/html/head//meta[@property="og:site_name"][@content]',
            'attribute' => 'content',
            'context' => 'title',
            'comment' => 'Open Graph Tag'
        ],
        [
            'selector' => '/html/head//meta[@property="og:description"][@content]',
            'attribute' => 'content',
            'context' => 'description',
            'comment' => 'Open Graph Tag'
        ],
        [
            'selector' => '/html/head//meta[@property="og:url"][@content]',
            'attribute' => 'content',
            'context' => 'slug|file|url',
            'comment' => 'Open Graph Tag'
        ],
        [
            'selector' => '/html/body//img[@src]',
            'attribute' => 'src',
            'context' => 'file',
            'comment' => 'Image urls'
        ],
        [
            'selector' => '/html/body//img[@srcset]',
            'attribute' => 'srcset',
            'context' => 'file',
            'comment' => 'Image srcset urls'
        ],
        [
            'selector' => '/html/body//picture//source[@srcset]',
            'attribute' => 'srcset',
            'context' => 'file',
            'comment' => 'Picture source srcset urls'
        ],
        [
            'selector' => '/html/body//*[contains(@style, "url(")]',
            'attribute' => 'style',
            'context' => 'file',
            'comment' => 'Background images'
        ],
        [
            'selector' => '/html/body//*[@label]',
            'attribute' => 'label',
            'context' => null,
            'comment' => 'Labels'
        ],
        [
            'selector' => '/html/body//@*[contains(name(), \'text\')]/parent::*', // *text*
            'attribute' => '*text*', // *text*
            'context' => null,
            'comment' => 'Text attributes'
        ],
        [
            'selector' => '.example-link', // css selectors are also supported
            'attribute' => 'alt-href|*foo*', // wildcards and | supported
            'context' => 'slug|file|url'
        ]
    ],
    'translate_html_exclude' => [
        ['selector' => '.notranslate', 'comment' => 'Default class'],
        ['selector' => '[data-context]', 'attribute' => 'data-context', 'comment' => 'Data context attributes'],
        ['selector' => '.lngpicker', 'comment' => 'Language picker'],
        ['selector' => '.xdebug-error', 'comment' => 'Xdebug errors'],
        ['selector' => '.example1', 'attribute' => 'data-text', 'comment' => 'Example'],
        ['selector' => '.example2', 'attribute' => 'data-*', 'comment' => 'Example']
    ],
    'translate_html_force_tokenize' => [['selector' => '.force-tokenize', 'comment' => 'Default class']],
    'localize_js' => false,
    'localize_js_strings' => ['Schließen', '/blog'],
    'detect_dom_changes' => false,
    'detect_dom_changes_include' => [
        ['selector' => '.top-button', 'comment' => 'Top button'],
        ['selector' => '.swal-overlay', 'comment' => 'SweetAlert']
    ],
    'translate_xml' => true,
    'translate_xml_include' => [
        [
            'selector' => '//*[name()=\'loc\']',
            'attribute' => null,
            'context' => 'slug',
            'comment' => 'Sitemap links'
        ]
    ],
    'translate_json' => true,
    'translate_json_include' => [
        ['url' => '/path/in/source/lng/to/specific/page', 'selector' => ['key'], 'comment' => 'Example'],
        [
            'url' => 'wp-json/v1/*/endpoint',
            'selector' => ['key', 'nested.key', 'key.with.*.wildcard'],
            'comment' => 'Example'
        ]
    ],
    'translate_wp_localize_script' => true,
    'translate_wp_localize_script_include' => [
        ['selector' => 'key1_*.key2.*', 'comment' => 'Example'],
        ['selector' => 'key3_*.key4', 'comment' => 'Example']
    ],
    'prevent_publish_wp_new_posts' => false,
    'url_query_args' => [
        [
            'selector' => '*',
            'type' => 'keep',
            'comment' => 'Keep everything'
        ],
        [
            'selector' => 'nonce',
            'type' => 'discard',
            'comment' => 'Discard nonces'
        ]
        /*
        // if you want to translate strings (be careful with dynamic content)
        [
            'selector' => 's',
            'type' => 'translate',
            'comment' => 'Dynamic search term translation'
        ]
        */
    ],
    'exclude_urls_content' => [['url' => 'backend', 'comment' => 'Backend']],
    'exclude_urls_slugs' => [['url' => 'api/v1.0', 'comment' => 'API']],
    'exclude_stopwords' => ['Some specific string to exclude'],
    'html_lang_attribute' => true,
    'html_hreflang_tags' => true,
    'xml_hreflang_tags' => true,
    'show_language_picker' => false,
    'show_frontend_editor_links' => false, // common wordpress configuration: 'show_frontend_editor_links' => user_is_logged_in()
    'debug_translations' => false, // surround outputted translations with "%|%" to see borders
    'auto_add_translations' => true,
    'auto_set_new_strings_checked' => false,
    'auto_set_discovered_strings_checked' => false,
    'unchecked_strings' => 'trans', // trans|source|hide
    'auto_translation' => true,
    'auto_translation_service' => [
        [
            'provider' => 'google', // google|microsoft|deepl or any other custom name
            'api_keys' => [], // use a string or array of strings to provide multiple keys at once
            'throttle_chars_per_month' => 1000000,
            'lng' => null, // ['en', 'fr'] populate if provider should act only on one or multiple specific languages
            'label' => null, // custom label
            'api_url' => null, // if you use your own custom translation provider, add here a custom GET api endpoint
            'disabled' => false
        ]
    ],
    'discovery_log' => false,
    'frontend_editor' => false, // common wordpress configuration: 'frontend_editor' => user_is_logged_in()
    'wp_mail_notifications' => false,
    'translate_wp_mail' => true
];

其他用法

Gtbabel会捕获start()stop()之间的内容,并使用输出缓冲
但是,您也可以更直接地使用Gtbabel

$gtbabel = new Gtbabel();

$gtbabel->config([...]);

$gtbabel->translate('<p>Dies ist ein Test!</p>'); // <p>This is a test!</p>
$gtbabel->translate('datenschutz', 'en', 'de', 'slug'); // data-protection

$gtbabel->tokenize('<p>Dies ist ein Test!</p>'); // [['string' => 'This is a test!', 'context' => null]]

WordPress插件

wordpress.org/plugins/gtbabel

您根本不需要在前端更改任何代码:如果您已经使用了像__()这样的函数,只需删除它们或者简单地保留(因为WordPress内部只了解源语言,__()没有效果);如果没有,请不要添加。Gtbabel作用于输出(就像在其他页面上一样)。

以下功能包含在内

  • 配置GUI
  • 简单的设置向导
  • 自动翻译(包括站点地图解析器)
  • 删除未使用的字符串
  • 检测共享字符串
  • 手动字符串翻译(通过URL搜索和筛选)
  • 翻译服务
  • 支持如Contact Form 7之类的插件
  • 翻译特定的帖子/页面或任何其他URL
  • 精细控制帖子/页面的发布状态
  • 通过媒体库按语言替换文件的可能性
  • 多站点支持
  • 基于能力的灵活权限系统
  • 防止不同缩略图大小的多次输入

权限

Gtbabel for WordPress使用以下能力

  • gtbabel__edit_settings:编辑设置的权限
  • gtbabel__email_notifications:接收电子邮件通知的权限
  • gtbabel__translation_list:使用翻译列表的权限
  • gtbabel__translation_assistant:使用翻译助手
  • gtbabel__translation_frontendeditor:使用前端编辑器
  • gtbabel__translate_xx:翻译代码为xx的语言

默认情况下,管理员拥有所有权限(除了gtbabel__email_notifications)。
您可以将这些权限单独添加到您的WordPress用户角色中

  • 在WordPress后端中,在Gtbabel > 权限下
  • 使用像User Role Editor这样的插件
  • 通过使用自定义代码。

以下示例为“编辑”角色在翻译助手中对英语和法语语言赋予了翻译权限

// only run this once
add_action('admin_init', function () {
    get_role('editor')->add_cap('gtbabel__translation_assistant');
    get_role('editor')->add_cap('gtbabel__translate_en');
    get_role('editor')->add_cap('gtbabel__translate_fr');
});

电子邮件通知

使用wp_mail_notifications选项,您可以指示Gtbabel向所有翻译者(具有gtbabel__translation_assistantgtbabel__email_notifications角色)发送定期的状态电子邮件

  • false:不发送状态电子邮件
  • 'hourly':每小时发送一次状态电子邮件
  • 'twicedaily':每天发送两次状态电子邮件
  • 'daily':每天发送一次状态电子邮件
  • 'weekly':每周发送一次状态电子邮件

请确保WP-Cron已启用并定期运行,邮件可以从您的服务器正确发送。

数据库和Gettext

  • 最终的HTML被解析,并分成合理的字符串。
  • Gtbabel在数据库层(例如sqlite)上运行,以确保快速加载时间。
  • 数据库结构反映了GNU gettext的结构。
  • 您可以在任何时候以po或xlsx文件的形式导出和导入数据。

JavaScript

Gtbabel本身基于PHP,适用于静态页面或通过PHP渲染的页面。

但是,如果您启用detect_dom_changes,Gtbabel会监视DOM更改并翻译合适的部分。

要直接在JavaScript中获得翻译,Gtbabel还提供了一个小型辅助函数,可以在每个环境中热加载您的翻译,该函数在标题中工作。为此,必须将选项localize_js_strings填充内容。然后您可以在JavaScript中轻松访问这些字符串

if (typeof gtbabel__ === 'function') {
    gtbabel__('Registered string');
}

如果您在使用WordPress,可能已经使用了wp_localize_script函数。继续这样做,并通过PHP使用gtbabel__()简单地翻译您希望在JavaScript中提供的字符串。

if (function_exists('gtbabel__')) {
    wp_localize_script('script', 'strings', [
        'baseurl' => gtbabel__(get_bloginfo('url')),
        'lng' => gtbabel_current_lng(),
        'example' => gtbabel__('Dies ist ein Test')
    ]);
}

另一种方法是使用translate_wp_localize_script选项:在translate_wp_localize_script_include中填充键链路径到(可能是嵌套的)字符串,Gtbabel会自动进行翻译。

修改过的节点

默认情况下,Gtbabel会根据以下合理的默认规则翻译所有文本和标签节点

您可以通过translate_html_include选项修改或添加新的标签节点转换。

例如,如果您想翻译所有数据属性,添加以下规则

[
    'selector' => '/html/body//@*[starts-with(name(), \'data-\')]/parent::*',
    'attribute' => 'data-*',
    'context' => null
];

或者,如果您想翻译特定的数据属性(并希望使用CSS选择器而不是XPath),可以使用

[
    'selector' => '.foo[data-bar]',
    'attribute' => 'data-bar',
    'context' => null
];

请注意,规则按顺序处理,属性名不会被转换两次。

Gtbabel会自动将合理部分分组。
以下代码被转换为1个标记(而不是3个)

<p>这是一个文本内的<a href="#">链接</a>。</p>

如果您想影响这种行为,请使用translate_html_force_tokenize选项,并提供父元素的选择器,以避免对其子元素进行标记。

如果您想影响特殊标签不应由Google/Microsoft/DeepL翻译API自动翻译,请使用特殊类

<p>Das ist das <span class="notranslate">Haus</span> vom Nikolaus</p>

标签前后部分会被翻译(并添加到数据库中)。
如果您想排除一个完整的节点被翻译(并不会添加到数据库中),请给父节点添加一个值为notranslate(或translate_html_exclude中定义的任何其他值)的类

<p class="notranslate">Das ist das <span>Haus</span> vom Nikolaus</p>

请注意,被忽略节点的属性也不会被翻译(href属性和文本Link不会被翻译)

<a href="/home" class="notranslate">Link</a>

然而,如果您想修改href属性,可以这样做

<a href="/home"><span class="notranslate">Link</span></a>

HTML

Gtbabel保留内联HTML标签,并将它们保留在您的翻译中。
但是,属性会自动去除。所以

<a href="https://tld.com" class="foo" data-bar="baz">Hallo</a> Welt!

被转换为

<a>Hallo</a> Welt!

该字符串被存储在翻译中,例如

<a>Hello</a> world!

这样,您的翻译者就不会被不必要的杂乱所困惑。
当然,翻译版本也有属性

<a href="https://tld.com" class="foo" data-bar="baz">Hello</a> world!

然而,如果顺序至关重要,Gtbabel提供了一个确定顺序(和重复)的机制。
例如

Das deutsche <a href="https://1.com">Brot</a> <a href="https://2.com">vermisse</a> ich am meisten.

在数据库中存储为

Das deutsche <a>Brot</a> <a>vermisse</a> ich am meisten.

如果您的翻译没有反映原始顺序,在数据库中会包含提示,例如

I <a p="2">miss</a> German <a p="1">bread</a> the most.

其中“2”代表原始字符串的第二个标签。

然后该字符串最终被正确地翻译为

I <a href="https://2.com">miss</a> German <a href="https://1.com">bread</a> the most..

请注意,在以下示例中,这些提示是不必要的(Gtbabel可以在不同的顺序下匹配标签)

Das deutsche <a href="https://1.com">Brot</a> <span>vermisse</span> ich am meisten.

歧义翻译和复数形式

考虑以下示例

<ul>
    <li>Bank</li>
    <!-- bank -->
    <li>Bank</li>
    <!-- bench -->
    <li>Schlüssel</li>
    <!-- key (singular) -->
    <li>Schlüssel</li>
    <!-- keys (plural) -->
</ul>

通常情况下,Gtbabel会从这段内容中创建2个翻译,并且无法区分模糊翻译和单复数形式。

为了达到这个效果,请使用特殊的data-context属性来强制合理的上下文

<ul>
    <li data-context="finance">Bank</li>
    <!-- bank -->
    <li data-context="furniture">Bank</li>
    <!-- bench -->
    <li data-context="singular">Schlüssel</li>
    <!-- key (singular) -->
    <li data-context="plural">Schlüssel</li>
    <!-- keys (plural) -->
</ul>

这样,您可以提供不同的翻译。

多种源语言

Gtbabel并不限制整个内容只使用一种源语言。
事实上,内容的一部分可以标记为特定语言

<!DOCTYPE html>
<html lang="en">
    <body>
        <p>Some content in english.</p>
        <div lang="fr">Contenu en français.</div>
        <p>Some other content in english.</p>
    </body>
</html>

这些内容都正确地翻译成了目标语言(在我们的案例中是de

<!DOCTYPE html>
<html lang="de">
    <body>
        <p>Einige Inhalte auf Englisch.</p>
        <div>Inhalt auf Französisch.</div>
        <p>Einige andere Inhalte in Englisch.</p>
    </body>
</html>

在WordPress中,有一种方便的方法来定义不同的源语言
用于整个帖子或页面,甚至是块级别。

隐藏内容

除了提供不同源语言的内容外,您还可以完全隐藏特定的DOM节点

<ul>
    <li>I am rendered everywhere</li>
    <li class="hide-block">I am missing everywhere (including source language)</li>
    <li class="hide-block-target">I am rendered only in the source language</li>
    <li class="hide-block-source">I am missing only in the source language</li>
    <li class="hide-block-en-fr">
        I am missing specifically in the languages `en` and `fr` and rendered everywhere else
    </li>
</ul>

注意,渲染表示在DOM中完全缺失。

这是(结合css类notranslate来阻止翻译和force-tokenize来强制分词以及lang属性)绕过Gtbabel旨在限制所有内容总是翻译成所有语言的尝试。

在WordPress中,您可以单独配置特定页面应翻译成哪些语言。对于已登录用户,还可以设置专有的视图——这允许您在内容向公众开放之前悄悄地准备翻译。

路由器

路由器自动修改$_SERVER['REQUEST_URI']变量以捕获翻译后的URL。
未知的URL翻译将从当前URL和页面上的链接中拾取。
这些URL将自动添加到数据库中(排除ajax请求的URL)。

域名架构

Gtbabel默认使用基于路径的方法来处理您的URL结构。
这意味着:语言代码始终附加到您的主URL之后

https://www.tld.com/de/

您可以根据喜好更改此行为。
只需扩展languages选项,添加url_baseurl_prefix
以下配置与未设置任何选项相同

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_base' => 'https://www.tld.com'
        'url_prefix' => 'de'
    ],
    [
        'code' => 'en',
        /* ... */
        'url_base' => 'https://www.tld.com'
        'url_prefix' => 'en'
    ]
    /* ... */
]

为了不使用源语言的前缀(这通常很常见),只需为它提供一条特殊的规则
(因为我们没有指定url_base和其他语言的任何选项,因为Gtbabel仍然在那里使用默认值)

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_prefix' => ''
    ],
    [
        'code' => 'en',
        /* ... */
    ]
    /* ... */
]

使用这种技术,我们可以在URL中使用自定义语言代码

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_prefix' => 'deutsch'
    ],
    [
        'code' => 'en',
        /* ... */
        'url_prefix' => 'english'
    ]
    /* ... */
]

或者我们可以轻松地设置基于子域的方法

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_base' => 'https://de.tld.com'
        'url_prefix' => ''
    ],
    [
        'code' => 'en',
        /* ... */
        'url_base' => 'https://en.tld.com'
        'url_prefix' => ''
    ]
    /* ... */
]

甚至可以基于多域名的方法

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_base' => 'https://www.tld.de'
        'url_prefix' => ''
    ],
    [
        'code' => 'en',
        /* ... */
        'url_base' => 'https://www.tld.com'
        'url_prefix' => ''
    ]
    /* ... */
]

如果您将您的网站托管在子路径上,您应该提供类似以下的内容

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_base' => 'https://www.tld.com/some/sub/path'
        'url_prefix' => 'de'
    ],
    [
        'code' => 'en',
        /* ... */
        'url_base' => 'https://www.tld.com/some/sub/path'
        'url_prefix' => 'en'
    ]
    /* ... */
]

如果您甚至不使用漂亮的链接,请留空所有前缀(然后URL查询参数lang决定)

'languages' => [
    [
        'code' => 'de',
        /* ... */
        'url_prefix' => ''
    ],
    [
        'code' => 'en',
        /* ... */
        'url_prefix' => ''
    ]
    /* ... */,
]

配置完全由您决定(您甚至可以混合方法)。
您只需确保所有顶级域名都指向相同的根路径。

处理资产

尽管Gtbabel未修改静态文件,但它会将它们添加到模板文件中(使用上下文file)。这为您提供了根据语言输出图片、下载和其他媒体的机会。

JSON响应

当将translate_json设置为true并填充translate_json_include时,Gtbabel也可以翻译JSON响应。
如果您的JSON端点不是通过特定语言URL访问的,您可以通过gtbabel_referer_lng()获取语言。

翻译管理

使用unchecked_strings选项,您可以控制未检查字符串的处理方式

  • trans:显示翻译(仍然需要检查)
  • source:显示源(以源语言显示字符串)
  • hide:隐藏字符串(完全隐藏字符串直到其被检查)

这样,您可以在后端构建一个审批系统,其中翻译者可以在内容发布之前检查字符串。

一种常见的方法是,如果用户已登录,则动态覆盖此设置。

/* ... */
'unchecked_strings' => is_user_logged_in() ? 'trans' : 'hide'
/* ... */

流程图

Flow diagram

语言选择器

要输出语言选择器,只需使用此代码片段。

if (function_exists('gtbabel_languagepicker')) {
    echo '<ul class="lngpicker">';
    foreach (gtbabel_languagepicker() as $val) {
        echo '<li>';
        echo '<a href="' . $val['url'] . '"' . ($val['active'] ? ' class="active"' : '') . '>';
        echo $val['label'];
        echo '</a>';
        echo '</li>';
    }
    echo '</ul>';
}

请确保将容器类排除在您的翻译之外。

辅助函数

始终用 if( function_exists('...') ) { } 将这些辅助函数包围。

gtbabel_current_lng() // 'en'
gtbabel_source_lng() // 'de'
gtbabel_languages() // ['de','en']
gtbabel_default_language_codes() // ['de','en','fr','af','am','ar','az','be','bg','bn','bs','ca','ceb','co','cs','cy','da','el','eo','es','et','eu','fa','fi','ga','gd','gl','gu','ha','haw','he','hi','hmn','hr','ht','hu','hy','id','ig','is','it','ja','jv','ka','kk','km','kn','ko','ku','ky','la','lb','lo','lt','lv','mg','mi','mk','ml','mn','mr','ms','mt','my','ne','nl','no','ny','pa','pl','ps','pt','ro','ru','sd','si','sk','sl','sm','sn','so','sq','sr','su','sv','ta','te','tg','th','tr','uk','ur','uz','vi','xh','yi','yo','zh-cn','zh-tw','zu']
gtbabel_default_languages() // [['code' => 'de', 'label' => 'Deutsch'], ['code' => 'en', 'label' => 'English'], ...]
gtbabel_language_label('en') // 'English'
gtbabel_default_settings() // ['languages' => [['code' => 'de', 'label' => 'Deutsch'], ['code' => 'en', 'label' => 'English'], ...], 'log_folder' => '/logs', ...]
gtbabel_default_settings(['log_folder' => '/foo']) // ['languages' => [['code' => 'de', 'label' => 'Deutsch'], ['code' => 'en', 'label' => 'English'], ...], 'log_folder' => '/foo', ...]
gtbabel_referer_lng() // 'de'
gtbabel_languagepicker() // [['code' => 'de', 'label' => 'Deutsch', 'url' => 'https://tld.com/de/nudel', 'active' => true], ['code' => 'en', 'label' => 'English', 'url' => 'https://tld.com/en/noodle', 'active' => false], ...]
gtbabel__('Hallo') // 'Hi there'

如你所见,这些辅助函数是静态函数,所以在一个正常环境中,当你调用这些函数时,会派生基于路由的设置,并且每次都会隐式地创建一个新的 Gtbabel 实例。然而,如果你想再次使用之前的实例,请将其声明为 global

global $gtbabel;
$gtbabel = new Gtbabel();
$gtbabel->config(['lng_source' => 'de', 'lng_target' => 'ar');
gtbabel__('Hallo'); // مرحبا

回声字符串

不要在模板中使用 gtbabel__() 输出字符串,只需在源语言中输出它们。Gtbabel 将处理其余部分。但是,在某些地方(例如在非常具体的场景中,在你的前端或后端逻辑中),你需要翻译字符串。这就是这个函数的目的所在。

一个例子是动态生成的 Facebook 分享链接,其中 Gtbabel 的解析器无能为力。你可以通过以下方式提供帮助:

$url = gtbabel__('https://gtbabel.com');
echo '<a href="https://#/sharer/sharer.php?u=' . urlencode($url) . '">Facebook</a>';

你还可以手动设置目标和源语言,以获取特定的翻译。因为翻译不是单射的,所以以下两个调用创建了两个具有不同源语言的数据库条目。

gtbabel__('Hallo', 'en', 'de') // 'Hi there'
gtbabel__('Hi there', 'de', 'en') // 'Hallo'

你可以向 gtbabel__() 提供任何 HTML。

gtbabel__('<p>Hallo <a href="/datenschutz">Datenschutz</a>!</p>'); // '<p>Hello <a href="/en/data-protection">data protection</a>!</p>'

这个函数足够智能,可以检测 URL 或甚至是根相对 URL,并返回适当的翻译。

gtbabel__('/datenschutz'); // '/en/data-protection'

然而,你也可以在特定上下文中获取翻译。

gtbabel__('datenschutz', null, null, 'slug'); // 'data-protection'

动态数据

Gtbabel 翻译你的前端、JSON 响应并监视 DOM 变化。
然而,必须手动处理如何进一步处理多语言输入数据。

搜索

在翻译内容上进行搜索很难,但可以通过不同的方法解决。

方法 1:翻译搜索词

一种相当有效且简单的方法是将所有传入的搜索查询翻译成你的源语言。
在实际上查询你的内容(以源语言提供)之前

$s = gtbabel__($s, gtbabel_source_lng(), gtbabel_current_lng(), 'search_term');

如你所见,此类搜索查询获得特殊的上下文 search_term,以便以后可以过滤或垃圾收集。

这里的缺点是,你实际上翻译了所有传入的查询,所以请注意这一点并使用 throttle_chars_per_month 选项。

结果也可能不准确,因为之前未见过的查询始终是即时翻译的。此外,它不适用于多源语言的内容。

方法 2:在解析的 HTML 中搜索

另一种方法是搜索生成的缓存 HTML 页面。
由于 Gtbabel 不缓存页面,你必须有你的缓存系统运行。

翻译的页面基本上可以像源语言页面一样处理,
因为它们都是 HTML,可以搜索。

还可以评估是否可以尝试使用 Google Programmable Search Engine,它遵循相同的理念。

方法 3:构建自定义搜索索引

在这个方法中,你为你要包括在搜索结果中的每个对象
构建一个搜索索引。

你收集并永久存储所有源语言字符串,
通过 Gtbabel 翻译它们(一次)并永久存储这些翻译。

这样,你可以结合上述方法的最佳之处,
尽管这需要最大的集成努力。

方法 4:在翻译中搜索

一种方法是用搜索词(不是源语言)
在翻译内容中进行搜索。

你必须手动修改你的搜索查询 - 在 SQL 中这可能看起来像这样

之前

SELECT posts.id, posts.col1, posts.col2
FROM posts
WHERE posts.col1 = 'English search term' OR posts.col2 = 'English search term';

之后

SELECT posts.id, posts.col1, posts.col2
FROM posts
LEFT JOIN translations ON translations.lng_target = 'en' AND (translations.str = wp_posts.col1 OR translations.str = wp_posts.col2)
GROUP BY posts.ID
HAVING FIND_IN_SET('English search term', GROUP_CONCAT(translations.trans));

这个想法可以大幅扩展(例如,通过使用正则表达式模式)。
请注意,这同样适用于不同源语言的内容。
在WordPress中,这种方法已经实现并且可以自动工作——提供对所有元字段的全文搜索。

URL查询参数

至于URL查询参数,您可以通过url_query_args选项精细控制参数是否保留、翻译或丢弃。

请注意:如果您将键设置为translate,外部内容可能会自动翻译。

此外,URL哈希(以#开头)总是保留。

请注意,电子邮件链接的查询参数会自动翻译

<a href="mailto:?subject=Dies%20ist%20ein%20Test&amp;body=Das%20funktioniert">发送电子邮件</a>

将被转换为

<a href="mailto:?subject=This%20is%20a%20test&amp;body=That%20work%27s">发送电子邮件</a>

邮件

如果您从后端发送邮件,请确保正确翻译主题和正文

$body = gtbabel__($body);
$message = gtbabel__($message);

如果邮件包含个人信息,请确保以HTML格式发送这些邮件,并适当排除动态内容

// before
$message = 'Hello John Doe,';
// after
$message = 'Hello <span class="notranslate force-tokenize">John Doe</span>,';

Gtbabel自动翻译在WordPress中发送的所有邮件,并内置了对
流行插件如Contact Form 7WooCommerce的支持,以确保上述指南。

评论

如果您在网站上提供评论功能,只需保存以输入语言(可通过gtbabel_current_lng()gtbabel_referer_lng()确定)接收到的评论。然后仅输出与当前输入语言匹配的评论,或者使用lang-标签自动翻译所有评论(见多个源语言)。

语言代码

建议使用小写ISO语言代码。
但您可以使用您想要的任何语言代码(甚至是i-klingon)。
对于每种语言,您应该提供用于自动翻译的语言代码。如果您的翻译服务不支持某种语言,请使用null
国家/地区代码(如“pt_BR”中的“BR”)应在(唯一的)code属性中使用短横线:pt-br。Gtbabel默认不区分英国英语和美国英语,但您可以简单地添加新语言或指示Gtbabel使用特定的语言代码(例如,用于自动翻译)。
hreflang代码必须在ISO 639-1中,因此某些语言在设置数组中没有官方的hreflang代码。

Google翻译API

  • 转到Google API控制台
  • 创建一个新的项目
  • 市场 > 启用“云翻译API”(这需要您设置一个账单账户)
  • API和服务 > API凭证 > 添加新的API密钥

API使用费用

Google、Microsoft或DeepL的翻译API可能很昂贵。尽量跟踪您当前的用量统计。Gtbabel通过跟踪翻译字符总数来帮助您。您还可以提供多个API密钥,Gtbabel然后将调用均匀分配。

您可以使用throttle_chars_per_month选项限制字符数。

自定义翻译提供者

如果您不想使用翻译的三大巨头,您可以在auto_translation_service字段中指定特殊的API URL。一个示例条目如下所示

[
    'provider' => 'custom_provider',
    'api_keys' => ['1337'],
    'throttle_chars_per_month' => 1000000,
    'lng' => null,
    'label' => 'My custom provider',
    'api_url' => 'https://tld.com/api/?str=%str%&from=%lng_source%&to=%lng_target%&api_key=%api_key%'
    'disabled' => false
]

Gtbabel然后将向该URL发起GET请求并自动填充占位符。
它然后可以接受和处理常见的JSON请求响应。

开发设置

单元测试

  • .env.example复制到.env并填写API凭证
  • composer install
  • ./vendor/bin/phpunit

集成测试

  • .env.example复制到.env并填写测试URL
  • 将vhost指向/tests/integration
  • 在浏览器中测试

构建管道

  • npm install
  • npm run dev / npm run prod

注意事项

  • gtbabel擅长翻译整个页面。如果您想为不同语言提供不同的内容,gtbabel可能不是合适的工具(但是您可以通过以下代码使用它:if( gtbabel_current_lng() === 'other-lng' ) { echo 'source language content'; }以及模板中关于<强>多种源语言的概念来实现这一点)。
  • 源语言是其他所有语言的基础(但是,您也可以用不同的源语言标记页面的一部分)。
  • gtbabel的设计是为了无需更改代码即可翻译页面的每个部分(但是您可以使用translate_html_exclude选项排除翻译)。

致谢

向我的同事致敬,没有他,这个项目就不会启动。