构建 Laravel 翻译包 - 前端构建
发布时间:作者: Joe Dixon
在上一篇文章中,我向您介绍了我的过程,其中包括将构建前端所需的一切都准备就绪。这篇文章将在此基础上完成该包的用户界面的构建。
用户界面应该做什么?
首先,我们需要定义任务的范围。用户界面的目的是什么,它应该允许用户做什么?有时,对于这类任务,限制范围可能具有挑战性。幸运的是,对我而言,这是一个我一直需要的工具,因此我非常清楚解决我问题的最低需求。
- 列出所有语言
- 创建新语言
- 列出一种语言的所有翻译
- 切换翻译的活动语言
- 搜索所有键和翻译
- 添加新的翻译
- 更新现有翻译
正如我在上一篇文章中提到的,我很乐意主要使用 Laravel 后端来构建此工具,以便处理繁重的工作。说我老派也行,但我真的喜欢页面加载的视觉提示,这样让我知道正在发生一些事情。我唯一可能不想看到页面加载的地方是更新现有翻译时 - 如果这可以在后台发生,会更优雅一些。
路由
根据上面的列表,看起来我们需要定义七条路由。
- GET /languages
- GET /languages/create
- POST /languages
- GET /languages/{language}/translations
- GET /languages/{language}/translations/create
- POST /languages/{language}/translations
- PUT /languages/{language}/translations
控制器
在控制器中,我们大量依赖于本系列文章早期构建的类。实际上,我们将在控制器的构造函数中从 Laravel 的容器中解析它。
public function __construct(Translation $translation){ $this->translation = $translation;}
现在,与控制器中的翻译进行交互是一个相对简单的任务。例如,获取要传递给视图的语言列表就像这样简单
public function index(Request $request){ $languages = $this->translation->allLanguages(); return view('translation::languages.index', compact('languages'));}
注意您可能会注意到上面的非同寻常的视图路径包含 translation::
。这只是告诉 Laravel 从我们在服务提供商中定义的包命名空间加载视图。
$this->loadViewsFrom(__DIR__.'/../resources/views', 'translation');
唯一复杂的控制器方法是翻译索引。之所以复杂,是因为我们将不仅列出翻译,而且还将添加搜索和过滤功能。
$translations = $this->translation->filterTranslationsFor($language, $request->get('filter')); if ($request->get('group') === 'single') { $translations = $translations->get('single'); $translations = new Collection(['single' => $translations]);} else { $translations = $translations->get('group')->filter(function ($values, $group) use ($request) { return $group === $request->get('group'); }); $translations = new Collection(['group' => $translations]);} return view('translation::languages.translations.index', compact('translations'));
在这里,我们使用用户传递进来的搜索词从翻译服务中获取过滤后的翻译。然后,我们确定是否需要仅过滤 single
或 group
类型的翻译,并相应地过滤返回的集合,然后再传递给视图。
视图和资产
每个视图都从一个主要的 layout.blade.php
文件扩展而来,该文件包含 HTML 脚手架以及指向使用Laravel Mix构建的 Javascript 和 CSS 资产的链接。
样式
对于那些不知道的人来说,Tailwind CSS 是一个以实用程序优先的框架。它提供一系列低级别类,可以将它们组合在一起以创建复杂的设计。请查看文档以获取更多信息。以下是我利用 Tailwind 强大功能的方法。
Tailwind 附带一个配置文件,该配置文件可以轻松设置基本颜色调色板和默认字体。该文件附带一些非常不错的默认设置,因此,除非必须引入一些品牌,否则我通常不会进行任何更改。
通常,我将首先将实用程序类应用于我的 HTML 元素,直到它看起来像我想要的那样。如果这是一个我不需要重用的元素,我通常会将它保留在标记中。
<div class="bg-red-lightest text-red-darker p-6 shadow-md" role="alert"> <div class="flex justify-center"> <p>{!! Session::get('error') !!}</p> </div></div>
但是,如果它是我将在应用程序中的多个地方重用的东西,我将使用 Tailwind 的 @apply
指令将实用程序提取到一个自定义组件类中。
// before<div class="p-4 text-lg border-b flex items-center font-thin"> {{ __('translation::languages.languages') }}</div> // after.panel-header { @apply p-4 text-lg border-b flex items-center font-thin} <div class="panel-header"> {{ __('translation::languages.languages') }}</div>
在上一篇文章中,我解释了如何将 PostCSS 和 Tailwind 的自定义 PostCSS 插件连接到 Laravel Mix 的构建管道中。当此插件运行时,它将查找项目 CSS 文件中的 @apply
指令,拉取它找到的实用程序中的样式,并将它们注入到包含类中。
以下图像显示了使用 Tailwind 创建的用户界面的样式。
Javascript
如本系列文章中之前提到的,我们将让后端处理此项目的大部分繁重工作。这样,我们就可以专注于只在 Javascript 增强用户体验的地方使用它。
翻译内容可能是一项繁琐的任务,因此我们希望确保翻译体验对我们的用户来说尽可能流畅和简化。我认为 Javascript 可以提供帮助的一个领域是,当用户浏览翻译列表时自动保存内容。这样可以避免额外的按钮点击和页面重新加载,这在这些情况下绝对没有必要。为了进一步增强体验,我们将创建一个视觉指示,让他们知道更改已应用。
为此,我们将创建一个名为 TranslationInput
的新 Vue 组件,它将为我们处理此功能。
export default { props: ['initialTranslation', 'language', 'group', 'translationKey', 'route'], data: function() { return { isActive: false, hasSaved: false, hasErrored: false, isLoading: false, hasChanged: false, translation: this.initialTranslation } }, ....}
在这里,我们定义了组件应该在每次使用时接收 initialTranslation
、语言、语言组、翻译键和路由作为属性。这提供了使用我们之前创建的 update
路由保存翻译所需的所有数据。
数据属性在组件上设置了一些状态。重要的是要注意,我们将 translation
设置为传递给组件作为道具的 initialTranslation
的值。
在组件模板中,我们将 translation
属性的值绑定到输入的值。
<textarea v-model="translation" v-bind:class="{ active: isActive }" v-on:focus="setActive" v-on:blur="storeTranslation"></textarea>
此外,当数据对象的 isActive
属性为真时,我们将输入类设置为活动状态。当输入获得焦点时,它由 setActive
方法处理。
setActive: function() { this.isActive = true;},
当用户从输入中导航离开时,我们希望使用用户的更改调用我们的 update
端点。您可以在上面看到我们使用 Vue 的 v-on
指令来监听 blur
事件,该事件调用 storeTranslation
方法。正是在此方法中,我们进行了该调用。
storeTranslation: function() { this.isActive = false; this.isLoading = true; window.axios.put(`/${this.route}/${this.language}`, { language: this.language, group: this.group, key: this.translationKey, value: this.translation }).then((response) => { this.hasSaved = true; this.isLoading = false; this.hasChanged = false; }).catch((error) => { this.hasErrored = true; this.isLoading = false; })}
在这里,我们使用axios(一个用于 JavaScript 的 HTTP 客户端)来调用我们的 update
端点。我们使用传递给组件的所有道具来生成 URL 和用户希望保存的最新翻译。
我们根据调用的结果更新组件的状态,并向用户提供一个视觉指示,以表明他们的操作是否成功。我们通过渲染一个适当的 SVG 图标来实现这一点。
<svg v-show="!isActive && isLoading">...</svg> // default state, pencil icon<svg v-show="!isActive && hasSaved">...</svg> // success state, green check icon<svg v-show="!isActive && hasErrored">...</svg> // error state, red cross icon<svg v-show="!isActive && !hasSaved && !hasErrored && !isLoading">...</svg> // saving state, disk icon
在这篇文章中,我们浏览了前端构建中最重要的组件。我们创建了一个不仅外观美观,而且对用户来说功能完善的界面。
在本系列文章的下一部分,我们将构建扫描项目以查找可能缺少语言文件中的翻译的能力。同时,如果您有任何问题,请随时在Twitter上联系我们。