Featured image of post 让你的 hugo 博客实现多语言

让你的 hugo 博客实现多语言

中英双语博客

前言

实现

languages:
  zh-cn:
    languageName: 中文
    weight: 1
  en:
    languageName: English
    title: "seth-shi"
    weight: 2
  • 然后加一个切换语言按钮layouts/partials/sidebar/left.html
<li id="lang-switcher">
    {{ range .Site.Languages }}
    <a data-url="{{ .Lang | relURL }}"
       href="javascript:void(0);"
       class="lang-item {{ if eq .Lang $.Site.Language.Lang }}active{{ end }}"
    >
        {{ .LanguageName }}
    </a>
    {{ end }}
</li>
  • 这个文件的最后加入js脚本(用于根据用户选择的语言自动跳转到对应的语言首页)
<script
const langOptions = {{ .Site.Languages }}.map(l => l.Lang)
const langStoreKey = '__lang__';
const defaultLang = 'zh';
const homePages = langOptions.map(l => `/${l}`).concat('/');

document.addEventListener('DOMContentLoaded', function() {
    initLangHomePage();

    // 点击切换语言
    listenLangSwitchEvent()
});

function listenLangSwitchEvent() {
    const currentLang = localStorage.getItem(langStoreKey);
    const currentPathList = window.location.pathname.split('/').filter(v => v.length > 0);
    document.querySelectorAll('#lang-switcher a').forEach(link => {
        link.onclick = function() {
            // 这里可以添加具体的 onclick 逻辑
            let lang = this.getAttribute('data-url').replace('/', '');
            localStorage.setItem(langStoreKey, lang);
            // /{currentLang?}/posts/xxx => /{lang?}/posts/xxx
            const langRedirectPathList = Array.from(currentPathList);
            if (langOptions.includes(langRedirectPathList[0])) {
                langRedirectPathList[0] = lang;
            } else {
                langRedirectPathList.unshift(lang);
            }

            if (langRedirectPathList[0].includes(defaultLang)) {
                langRedirectPathList.shift();
            }

            langRedirectPathList.unshift('');
            langRedirectPathList.push('');
            window.location.href = langRedirectPathList.join('/');
        };
    });
}

function initLangHomePage() {
    const currentPath = window.location.pathname;
    if (! homePages.includes(currentPath)) {
        return;
    }

    const redirectPath = getInitRedirectPath('zh')
    console.log(`redirect:${redirectPath}`);
    if (redirectPath !== currentPath) {
        window.location.href = redirectPath;
    }
}


function getInitRedirectPath() {
    let lang = localStorage.getItem(langStoreKey);
    if (lang === null) {
        // from default browser language
        lang = navigator.language.substring(0, 2);
    }

    let mapLang = langOptions[0];
    const index = langOptions.findIndex(l => l.includes(lang))
    if (index >= 0) {
        mapLang = langOptions[index];
    }

    let redirectPath = `/${mapLang}`;
    if (mapLang.includes(defaultLang)) {
        redirectPath = '/';
    }

    return redirectPath;
}
</script>

翻译博客内容

  • 对应的分类,标签, 内容按照规则命名就可以(比如是index.md,复制一个文件命名为index.en.md)
  • 我的博客使用GPT去翻译全部的, 脚本如下
<?php


$rootDir = __DIR__ . '/content/posts';
$dir = opendir($rootDir);
$postsDir = [];
while (($filename = readdir($dir)) !== false) {
    // 务必使用!==,防止目录下出现类似文件名“0”等情况
    if ($filename !== "." && $filename !== "..")
    {
        $postsDir[] = $filename ;
    }
}

info(sprintf("total dir: %d", count($postsDir)));
foreach ($postsDir as $i => $dir) {
    $index = "{$rootDir}/$dir/index.md";
    $enIndex = "{$rootDir}/$dir/index.en.md";
    if (! file_exists($index)) {
        info("error {$index}");
        continue;
    }

    if (file_exists($enIndex)) {
        info("exists {$enIndex} skip");
        continue;
    }

    $content = file_get_contents($index);
    $res = chat($content);
    $len = strlen($res);
    if ($len > 0) {
        file_put_contents($content, $enIndex);
    }
    info("{$i} success [{$len}]{$enIndex} skip");
}


function info($msg) {
    echo $msg . PHP_EOL;
}

function chat($content, $model = 'gpt-40') {
    $apiKey = "YOUR_OPENAI_API_KEY";

    $endpoint  = 'https://api.openai.com/v1/chat/completions';

    $data = [
        "messages" => [
            ["role" => "system", "content" => "按照 hugo 的格式, 把文件的标题, 描述, tags, 还有内容翻译成英文, 遇到专业名词或者是英文的, 跳过这些关键字"],
            ["role" => "user", "content" => $content]
        ],
        "model" => $model,
    ];

    $headers = array(
        'Content-Type: application/json',
        'Authorization: Bearer ' . $apiKey
    );

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $endpoint);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data,JSON_UNESCAPED_UNICODE));
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    $response = curl_exec($ch);

    curl_close($ch);

    $decodedResponse = json_decode($response, true);
    $ret = '';
    if (isset($decodedResponse['choices'][0]['message']['content'])) {
        $ret = $decodedResponse['choices'][0]['message']['content'];
    }
    return $ret;
}

效果