Mobile App Localization - A Developer’s Illustrated…

移动应用程序本地化-开发人员的插图

2023-05-30 00:00 GALA

本文共1571个字,阅读需16分钟

阅读模式 切换至中文

Sign up for our newsletter on globalization and localization matters. In the first part of this series we looked at all the popular mobile app frameworks, and briefly described Flutter’s first-class support for quickly creating localized apps. Let us now examine this capability in more detail and discuss how it allows developers to move localization from an afterthought to something that’s built into the app from the outset. We’ll do that by looking at an actual localized application that caters to scuba divers looking for dive sites all around the world. My goal with this article is to help you learn more about Flutter and its capabilities when it comes to localization, as well as how CI/CD best practices can be integrated into your development process to handle updates to your app content. We’ll do this by outlining the steps to achieve a translated version of your app with Flutter, and then take a look at how you can use continuous delivery to keep your translations up-to-date. To keep in theme, let us dive into the code! Localizing with Flutter Broadly speaking, implementing localization in Flutter can be broken down into four steps (and a plus-one): Write the app Translate the content Build the app Enjoy the localized UI Handle updates Of course, there are nuances to all of these, especially the first one. In this section, we’ll take a look at the steps required to implement localization in a Flutter application, complete with code samples - from a real application, not some made up scenario! - so that you can see and understand the effort (or lack thereof) required to make an application localizable. Writing a localizable app Where other frameworks require lengthy preparation and boilerplate to enable localization, Flutter is now particularly streamlined in this regard. Before you start writing localized content or wiring up Flutter’s built-in localization subsystem, the app must first declare a dependency on the feature. You do this in the project’s main configuration file - pubspec.yaml - by adding a reference to the library (and activating code generation) with just 3 lines of code: You’ll also need to configure the localization system. While there are many options to fine-tune the process, a minimalist approach can be taken with just three lines of configuration in a special file named l10n.yaml: There is some preparation to do, most importantly declaring your supported locales so that Flutter knows what files it needs to load. This should really be done where you initialize your application UI, and the Flutter framework includes a whole set of dedicated properties for this purpose. The practical upshot of this way of declaring your language support is that the Flutter toolchain will now check for the existence and validity of your localization files. If you forget to create even one of them, the build will fail and you won’t be able to release a partially-localized version of your cherished app. Now that the localization subsystem is initialized, you need to write (or move out of your code) the content that will be displayed. Early on in the process, you do this by writing the ARB files directly, or by using third-party tools to extract your content. Because it is JSON at its core, ARB is fairly easy to write, and can be enhanced to include a variety of data for the translators handling it, from descriptions of the individual parts to parameter explanations, to even screenshots - using simple Base64 encoding - that other tools can read and display. This means that the translators working on this content can be instructed on specific points in a way that isn’t always possible when working with other formats. Once you’ve written the ARB file, you need a way to display its content. You do this by referencing the keys of the ARB file in your app code. If you used parametric strings - as I did for this filter button - you can pass the parameters for the string just like any other function parameter, making it very easy to construct even complicated sentences in a way that’s conducive to localization. Now that you have the app wired up for localized display, all that remains to do is to build it. If you’ve followed the steps leading up to this point, all you need to do is run the build command ($ flutter build [ios|appbundle|web|macos|windows|linux]). If everything goes according to plan, you’ll have a fully localized application when the build runner finishes - or an error and a failed build if you’re missing one of the languages declared in your code, which ensures that you don’t accidentally miss one of them and release a partially-localized version. Now all you have to do is ship the application to the stores, and start working on the next exciting feature. Of course, localization is not a one-time thing. Like each feature of the app, it needs to be maintained. With each feature, with each change, you’ll have new content to localize, your translators will need to keep track of said content, and your build process will need to incorporate the new translations into the packaged app. The practical upshot of Flutter’s approach to storing the localized content in separate files is that every change to the source or the translations creates a new commit, triggering your CI/CD process. With the proliferation of content connectors for source repositories and the easy processing of ARB files, integrating continuous translation into your regular CI/CD pipeline is hardly a challenge. Handling the Localization The Flutter framework uses ARB files to store its localizable content. ARB stands for Application Resource Bundle, a format created by Google to hold many forms of content in a single container, ready for locale-specific transformation. Of particular note to developers is ARB’s ability to contain not only template strings - messages with placeholders that can be replaced at runtime - but also to defuse some of the more complicated issues of creating a localized app, such as plurals and gendered terms, which require notorious amounts of boilerplate code when hand-rolled (and are just as easy to get wrong). In addition to the thorny issues of pluralization and gendered languages, one of the worst problems that plague front-end engineers when it comes to localization is the various cultural differences, such as text length and writing direction. As an example of text expansion, consider the differences between an English source and its Japanese and German translations: where Japanese can be as little as 60% of the length of the original, German can be up to 130% the length. To give a deliberately extreme example, imagine a fictional rural retirement village for elderly government officials in the U.S, Japan, and Germany: It’s easy to see how the three languages require vastly different layouts to accommodate the same concepts, effectively tripling the design and coding effort. When it comes to text direction, it’s not hard to imagine just how many issues can arise when translating from English to the various Arabic dialects, and having to reverse the entire display. This includes not only the text direction, but also the relative positions of elements. An Arabic reader will expect a menu bar to go from right to left, not vice versa. Or, to give an even more extreme example, an application aimed at elderly Japanese might be best if displayed in tategaki (writing top-to-bottom right-to-left) which requires not only rotating the layout, but also flipping it from right to left. Fortunately for the sanity of front-end developers, Flutter takes care of these issues natively. Its layout engine allows for fluid resizing and positioning of elements as text length changes, ensuring that - unless the developer actively works against the engine - the UI they create will look the same across all platforms, regardless of screen size or the content length. What’s more, because its engine is based on the concepts of main and cross axes, it’s relatively easy to manipulate element positions along those axes to ensure that your UI looks as good right-to-left as it does left-to-right. And when a right-to-left language is loaded, Flutter’s layout engine will automatically flip the direction of the main axis, so that an Arab user will see their culturally appropriate display being rendered in the correct order. Conclusions This was quite a journey through many topics, full of takeaways. If there’s one thing you, as a software engineer, take away from this article, let that be this: “Flutter as a framework is a safe, performant, and expressive system that allows fast and easy implementation of translations in new and existing apps”. As a developer, you’ll benefit from the strongly-typed Dart language which is fast and expressive, while preventing whole classes of bugs that can ruin the experience. Dart is also cross-platform, able to run the same code (for the most part) across all computing platforms. It also works with you to build your UIs and protects you from localization failures by not allowing you to release versions with missing translations. We’re always on the lookout for informative, useful and well-researched content relative to our industry. Write to us.
订阅我们的全球化和本地化新闻通讯。 在本系列的第一部分中,我们研究了所有流行的移动应用程序框架,并简要描述了Flutter对快速创建本地化应用程序的一流支持。现在让我们更详细地研究这个功能,并讨论它如何允许开发人员将本地化从事后的想法转移到从一开始就内置到应用程序中的东西。我们将通过查看一个实际的本地化应用程序来实现这一点,该应用程序满足了在世界各地寻找潜水地点的水肺潜水员的需求。 我写这篇文章的目的是帮助你更多地了解Flutter及其在本地化方面的功能,以及如何将CI/CD最佳实践集成到你的开发过程中,以处理你的应用内容的更新。我们将通过概述使用Flutter实现应用翻译版本的步骤来做到这一点,然后看看如何使用持续交付来保持翻译的最新状态。 为了保持主题,让我们深入代码! 使用Flutter定位 一般来说,在Flutter中实现本地化可以分为四个步骤(以及一个加一): 编写应用程序 翻译内容 构建应用程序 享受本地化的UI 处理更新 当然,所有这些都有细微差别,尤其是第一个。 在本节中,我们将看看在Flutter应用程序中实现本地化所需的步骤,并提供代码示例-来自真实的应用程序,而不是一些虚构的场景!- 这样您就可以看到并了解使应用程序可本地化所需的工作量(或不足)。 编写可本地化的应用 在其他框架需要冗长的准备和样板来实现本地化的地方,Flutter现在在这方面特别简化。 在开始编写本地化内容或连接Flutter的内置本地化子系统之前,应用必须首先声明对该功能的依赖项。您可以在项目的主配置文件pubspec.yaml中执行此操作,方法是添加一个对库的引用(并激活代码生成),只需3行代码: 您还需要配置本地化系统。虽然有许多选项可以微调该过程,但可以采用最低限度的方法,在名为l10n.yaml的特殊文件中仅使用三行配置: 有一些准备工作要做,最重要的是声明您支持的语言环境,以便Flutter知道它需要加载哪些文件。这真的应该在初始化应用程序UI的地方完成,Flutter框架包含了一整套专用的属性。 这种声明语言支持的方式的实际结果是,Flutter工具链现在将检查本地化文件的存在和有效性。如果您忘记创建其中一个,构建将失败,您将无法发布您心爱的应用程序的部分本地化版本。 现在本地化子系统已经初始化,您需要编写(或移出代码)将要显示的内容。在此过程的早期,您可以通过直接编写ARB文件或使用第三方工具提取内容来完成此操作。因为它的核心是JSON,所以ARB相当容易编写,并且可以增强以包含各种数据,供翻译人员处理,从单个部分的描述到参数解释,甚至是屏幕截图-使用简单的Base64编码-其他工具可以读取和显示。 这意味着,处理这些内容的翻译人员可以在特定的点上得到指导,这在处理其他格式时是不可能的。 编写ARB文件后,需要一种方法来显示其内容。您可以通过在应用代码中引用ARB文件的键来执行此操作。 如果你使用参数化字符串--就像我在这个过滤器按钮中所做的那样--你可以像传递任何其他函数参数一样传递字符串的参数,这使得以一种有利于本地化的方式构造复杂的句子变得非常容易。 现在,您已经为本地化显示连接了应用程序,剩下要做的就是构建它。如果您已经遵循了前面的步骤,那么您需要做的就是运行build命令($ flutter build [ios| appbundle|腹板|马科斯|窗口|linux])。如果一切按计划进行,当构建运行器完成时,您将拥有一个完全本地化的应用程序-或者如果您丢失代码中声明的语言之一,则会出现错误和失败的构建,这可以确保您不会意外地丢失其中一种语言并发布部分本地化版本。 现在,您所要做的就是将应用程序发送到商店,并开始开发下一个令人兴奋的功能。 当然,本地化不是一蹴而就的事情。像应用程序的每个功能一样,它需要维护。对于每个功能,每个更改,您都将有新的内容需要本地化,您的翻译人员将需要跟踪所述内容,并且您的构建过程将需要将新的翻译纳入打包的应用程序。 Flutter将本地化内容存储在单独文件中的方法的实际结果是,对源代码或翻译的每次更改都会创建一个新的提交,从而触发CI/CD过程。随着源存储库的内容连接器的激增和ARB文件的轻松处理,将连续翻译集成到常规CI/CD管道中几乎不是一个挑战。 处理本地化 Flutter框架使用ARB文件来存储其可本地化的内容。ARB代表Application Resource Bundle,这是Google创建的一种格式,用于在单个容器中保存多种形式的内容,准备进行特定于区域设置的转换。 开发人员特别注意的是,ARB不仅能够包含模板字符串(带有占位符的消息,可以在运行时替换),而且还能够解决创建本地化应用程序时的一些更复杂的问题,例如复数和性别术语,这些术语在手工操作时需要大量的样板代码(并且同样容易出错)。 除了语言多元化和性别化的棘手问题外,在本地化方面,困扰前端工程师的最糟糕的问题之一是各种文化差异,例如文本长度和写作方向。 作为文本扩展的一个例子,考虑英语源与其日语和德语翻译之间的差异:日语的长度可以是原文的60%,德语的长度可以是原文的130%。举一个故意极端的例子,想象一下美国、日本和德国的一个虚构的农村退休村,那里有老年政府官员: 很容易看出,这三种语言需要截然不同的布局来适应相同的概念,从而有效地将设计和编码工作增加了两倍。 当涉及到文本方向时,不难想象从英语翻译到各种阿拉伯语方言时会出现多少问题,并且必须颠倒整个显示。这不仅包括文本方向,还包括元素的相对位置。阿拉伯语读者会期望菜单栏从右到左,而不是相反。或者,举一个更极端的例子,一个针对日本老年人的应用程序可能最好以tategaki(从上到下从右到左)显示,这不仅需要旋转布局,而且还需要从右到左翻转布局。 幸运的是,对于前端开发人员来说,Flutter可以原生地处理这些问题。它的布局引擎允许随着文本长度的变化而调整元素的大小和位置,确保-除非开发人员主动与引擎对抗-他们创建的UI在所有平台上看起来都是一样的,无论屏幕大小或内容长度如何。更重要的是,因为它的引擎是基于主轴和横轴的概念,所以相对容易操纵元素沿着这些轴的位置,以确保您的UI从右到左看起来和从左到右一样好。当加载从右到左的语言时,Flutter的布局引擎将自动翻转主轴的方向,以便阿拉伯用户可以看到他们的文化适当的显示以正确的顺序呈现。 结论 这是一个相当多的主题之旅,充满了外卖。作为一名软件工程师,如果您能从本文中学到一件事,那就是:Flutter作为一个框架是一个安全,高性能和富有表现力的系统,可以在新的和现有的应用程序中快速,轻松地实现翻译。 作为一名开发人员,您将受益于强类型的Dart语言,它快速而富有表现力,同时防止可能破坏体验的整个bug类。Dart也是跨平台的,能够在所有计算平台上运行相同的代码(大部分)。它还与您一起构建UI,并通过不允许您发布缺少翻译的版本来保护您免受本地化失败的影响。 我们一直在寻找与我们行业相关的信息丰富、有用和经过充分研究的内容。 写信给我们。

以上中文文本为机器翻译,存在不同程度偏差和错误,请理解并参考英文原文阅读。

阅读原文