开始之前

你需要:

  • Node.js v20.11.1 或更高版本。
  • 电脑上的代码编辑器。
  • 一个 Directus 项目 - 如果你还没有,请按照我们的快速入门指南进行操作。 对 React 和 Svelte 有一定了解。

本教程的代码可在该 GitHub 仓库中获取。

安装 SvelteKit 并设置新项目

首先设置一个新的 Svelte 项目,并安装包括 Directus SDK 在内的所需依赖项:

npm create svelte@latest frontend # Select the Skeleton project
cd directus-i18n-app
npm install
npm install @directus/sdk

在 src/libs 目录中,创建一个 directus.js 文件以创建并导出 Directus SDK 实例:

import { createDirectus, rest } from '@directus/sdk';
import { PUBLIC_DIRECTUS_API_URL } from '$env/static/public';
function getDirectusInstance(fetch) {
  	const options = fetch ? { globals: { fetch } } : {};
	const directus = createDirectus(PUBLIC_DIRECTUS_API_URL).with(rest());
	return directus;
}
export default getDirectusInstance;

然后在项目的根目录中创建一个 .env 文件,并添加您的 Directus API URL:

PUBLIC_DIRECTUS_API_URL='https://directus.example.com';

设计数据模型

在 Directus 数据工作室中,导航到设置 -> 数据模型,并创建一个名为 news 的新集合:

  • slug (主键字段,类型:手动输入字符串)
  • author (类型: 字符串, 接口: 输入)
  • cover (类型:图像)

创建一个名为 languages 的集合:

  • code (主键字段,类型:手动输入字符串)
  • name (类型: 字符串, 接口: 输入)
  • direction (类型:字符串,界面:下拉菜单,选项: ltr 和 rtl 。默认值: ltr )

direction 字段启用对从右到左阅读的语言的支持。

要在您的 news 集合中启用内容翻译,请使用翻译界面创建一个 translations 字段。选择 name 作为语言指示字段, direction 作为语言方向字段,并将 en-US 设置为默认语言。

保存后,将为您创建一个名为 news_translations 的新集合。在 news_translations 集合中,您将添加需要翻译的字段。

将以下字段添加到 news_translations 集合中:

  • title (类型: 字符串, 接口: 输入)
  • body (类型: 文本, 界面: 所见即所得)

将您希望支持的每种语言作为项目添加到 languages 集合中。

news 系列的项目页面现在包含一个翻译界面。

允许公共角色在访问控制设置中读取 newslanguagesnews_translations 集合,以确保前端可以访问这些集合。

使用 SvelteKit 构建新闻应用前端

在您的 Svelte 项目中,更新您的 +page.js 文件以使用 SDK 获取内容:

import getDirectusInstance from "$lib/directus";
import { readItems } from "@directus/sdk";

export async function load({ fetch }) {
  const directus = getDirectusInstance(fetch);
  return {
    global: await directus.request(readItems("global")),
    news: await directus.request(readItems("news", {
        deep: {
          translations: {
            _filter: {
              _and: [
                {
                  languages_code: { _eq: "en-US" },
                },
              ],
            },
          },
        },
        fields: ["*", { translations: ["*"] }],
      })
    ),
  };
}

上述代码片段将使用:

  • readItems 函数用于获取新闻集合中的所有内容。
  • deep 参数用于过滤相关集合,仅显示 en-US(美国英语)的翻译。

更新 src 目录中 +page.svelte 文件的代码以渲染新闻:

<script>
  export let data;
</script>

<h1>Trending Today!</h1>
<ul>
  {#each data.news as article}
    <li>
      <div>
        <h2>
          <a href={`/${article.id}`}>
            {article.translations[0].title}
          </a>
        </h2>
        <p>By {article.author}</p>
      </div>
    </li>
  {/each}
</ul>

上述代码将:

  • 遍历 +page.js 文件中返回的新闻数组以显示内容。
  • 为每个新闻列表附加一个指向新闻详情页的链接。

在 routes 目录中创建一个 news/+page.js 文件,用于渲染单个新闻内容的路线:

import { readItem } from "@directus/sdk";
import getDirectusInstance from "$lib/directus";
import { error } from "@sveltejs/kit";

/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params, url }) {
  const directus = getDirectusInstance(fetch);

  const slug = params.slug;
  try {
    const [newsData, languagesData] = await Promise.all([
      directus.request(
        readItem("news", slug, {
          fields: ["*", { "*": ["*"] }],
        })
      ),
      directus.request(readItems("languages")),
    ]);

    return {
      article: newsData ? newsData : null,
      languages: languagesData,
    };
  } catch (err) {
    error(404, "Post not found");
  }
}

上述代码将:

  • 使用 readItem 函数查找并获取新闻集合中与主键字段(slug)匹配的新闻。
  • languages 集合中获取所有可用语言。

routes/news 目录中创建一个 +page.svelte 文件,并添加以下代码:

<script>
  export let data;
  $: ({ article, languages } = data);
</script>
{#if article}
  <h1>{article.translations[0].title}</h1>
  {@html article.translations[0].body}
  <select>
    {#each languages as language}
      <option value={language.code}>{language.name}</option>
    {/each}
  </select>
{:else}
  <p>News not found.</p>
{/if}

上述代码将:

  • 获取从 news/+page.js 文件返回的语言和选定的新闻文章数据并进行渲染。
  • 在选择字段中渲染语言,以便用户可以选择他们需要将内容翻译成的语言。
  • 使用 @html 装饰器以正确渲染 WYSIWYG body 字段内容。

添加多语言导航和搜索

更新您的项目以添加多语言导航和搜索功能。更新 routes/news/+page.svelte 文件中的代码,以添加处理程序,根据所选语言动态渲染文章翻译。

<script>
  import { goto } from '$app/navigation';
  export let data;
  $: ({ article, languages, languageCode } = data);

  let selectedLanguageCode = languageCode;

  function handleLanguageChange(event) {
    const newLanguageCode = event.target.value;
    selectedLanguageCode = newLanguageCode; // Update the selectedLanguageCode
    goto(`?lang=${newLanguageCode}`, { replaceState: true });
  }
</script>

{#if article}
  <h1>{article.translations[0].title}</h1>
  {@html article.translations[0].body}
  <select value={selectedLanguageCode} on:change={handleLanguageChange}>
    {#each languages as language}
      {console.log(language)}
      <option value={language.code}>{language.name}</option>
    {/each}
  </select>
{:else}
  <p>News not found.</p>
{/if}

然后,更新您的 routes/news/+page.js 文件中的代码,通过添加一个新的 URL 参数来允许用户动态选择所需的新闻翻译语言,并使用该参数来过滤新闻翻译。

import { readItem } from "@directus/sdk";
import getDirectusInstance from "$lib/directus";
import { error } from "@sveltejs/kit";

/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params, url }) {
  const directus = getDirectusInstance(fetch);

  const slug = params.slug;
  const languageCode = url.searchParams.get("lang") || "en-US";
  try {
    const [newsData, languagesData] = await Promise.all([
      directus.request(
        readItem("news", slug, {
          deep: {
            translations: {
              _filter: {
                _and: [
                  { languages_code: { _eq: languageCode } },
                ],
              },
            },
          },
          fields: ["*", { "*": ["*"] }],
        })
      ),
      directus.request(readItems("languages")),
    ]);

    return {
      article: newsData ? newsData : null,
      languages: languagesData,
      languageCode,
    };
  } catch (err) {
    error(404, "Post not found");
  }
}

现在您可以将新闻翻译成英语、德语和法语。

将以下代码片段替换您 routes/+page.svelte 文件中的代码,以添加搜索功能:

<script>
  import { goto } from "$app/navigation";
  import { page } from "$app/stores";
  export let data;

  let searchQuery = $page.url.searchParams.get("q") || "";

  function handleSearchChange() {
    goto(`/?q=${searchQuery}`, { replaceState: true });
  }
</script>

<h1>Trending Today!</h1>
<div>
  <input type="text" bind:value={searchQuery} placeholder="Search News..." />
  <button on:click={handleSearchChange}>Search</button>
</div>
<ul>
  {#each data.news as article}
    <li>
      <div>
        <h2>
          <a href={`/${article.id}`}>
            {article.translations[0].title}
          </a>
        </h2>
        <p>By {article.author}</p>
      </div>
    </li>
  {/each}
</ul>

上述代码将:

  • 定义变量 searchQuery 以存储用户的搜索输入。
  • 使用当前 URL ($page.url.searchParams.get("q")) 中 q 查询参数的值初始化 searchQuery 变量。如果 q 参数不存在,则默认为空字符串。
  • 使用 handleSearchChange 通过 $app/navigation 中的 goto 函数,用当前的 searchQuery 值更新 URL。 replaceState: true 选项将替换当前的历史记录条目,而不是创建新的条目。
  • 渲染一个输入框和一个按钮,允许用户输入搜索查询并触发搜索。
  • 显示搜索到的新闻,若未进行搜索则显示所有新闻。

摘要

在本教程中,您学习了如何使用 SvelteKit 和 Directus 构建一个多语言新闻应用程序。您已经设置了一个 SvelteKit 项目,创建了一个 Directus 包装器,并使用它来查询数据。我们通过 Directus 灵活的 CMS 创建了翻译集合,并使用翻译界面将新闻文章内容翻译成不同的语言。