国际化:使所有人都可以访问Web

Ecma International,技术委员会39,或简称为TC39,是由JavaScript开发人员,技术实施者,学者和其他感兴趣的团体组成的小组,他们与社区一起支持和开发JavaScript作为平台。

TC39参与者通常通过对JavaScript的深刻理解来分享一些有趣的东西。但是有些人认为他们与普通开发人员的问题相去甚远。语言开发人员在哪里?实践中每天写前端的人在哪里?

让我们熟悉该报告,报告结合了理解的深度和高度的实用性。了解Romulo Cintra关于国际化问题的新故事,该新故事将通过新API(即将在JavaScript中出现)解决。



Romulo Cintra -TC39代表,从事开发和体系结构工作已有10多年,专门研究Web,移动开发和云。在此报告中,您将直接学习MessageFormat工作组的联合主席,该解决方案已经可用于解决现有问题,以及将使用JavaScript本身中的新API以何种形式解决这些问题。

剪下的部分是Romulo报告的全文文字记录以及该视频的链接。如果您想阅读-这篇文章包含了所有内容,那么您将不会错过任何东西。如果您有时间开始录制视频,那么大约有一个小时的录制质量,其中包含有趣的幻灯片和可理解的英语。

代表讲者的进一步叙述。


关于国际化和本地化的状态,您需要了解三件事:一切都非常非常非常糟糕。我叫Romulo Cintra,参与金融体系结构应用程序。我与TC39的人们进行了很多交谈,看看他们如何尝试使JavaScript世界变得更好。另外,我是开源的坚定支持者,在业余时间,我是巴塞罗那技术学院的一名老师。

国际化问题非常重要。碰巧我们地球上有许多不同的民族和语言。当今世界上大约有195个国家/地区和6000种语言。这使我们的任务极为困难。想想别的事情:我用英语而不是俄语写文章和阅读报告。我们已经有一个国际化的问题。如果某人不会说英语,则将其排除在我们与您的对话之外。为了防止这种情况的发生,发明了国际化。

英语国际化缩写为i18n。数字18是此单词中字母i和n之间的字符数。简而言之,国际化是为了尽可能简化本地化的软件设计。由于国际化,该软件可以支持本地设置,语言,货币等。国际化使每个人都可以访问网络。在这里,您可以通过测试(测试驱动的开发)与开发进行并行处理:首先编写测试,然后编写代码;随着国际化,有必要做同样的事情。通常人们会在编写代码后考虑国际化,但这是错误的。

与i18n相似,l10n是本地化的缩写,10是第一个l和最后一个n之间的字符数。本地化是指产品适应其所分布的语言和文化环境。也就是说,您不仅需要将“ Hello”转换为“ Hello”,还需要使用本地货币,小数点分隔符等,即使用户更加熟悉该软件。它不只是翻译。

您的Web项目支持几种语言?许多有两个以上。有没有五个以上的人?那15点呢?我们支持约25种语言。我们没有很好的支持,因为国际化不是以最佳方式组织的。在报告过程中,我将解释如何改善国际化,并讨论我们正在采取的措施。

因此,我再次重复:国际化意味着简化本地化,并在体系结构级别上为其提供支持。本地化是使软件适应当地实际情况。译文通常与原始译文不符-以电影行业为例,该电影的名称“ Pain and Gain”(痛苦与收获)被翻译为“ Blood and Sweat:Anabolics”。



或另一个例子:俄罗斯浴的广告,其中英文翻译为“ Russian火葬场”(Russian火葬场)。



我怀疑这是否会吸引顾客,至少是活着的。国际化和本地化之所以如此重要,正是因为它们使我们能够准确地向用户传达我们想要说的话。本质上,国际化是提供可访问性,因为如果您不了解所使用的软件,那么实际上您的选择将受到限制。该软件易于使用对每个人都是有益的,因为它提供了更广泛的用户,因此收入更高;它还可以改善网络。

讯息格式


考虑一下代码示例:

'es-ES': { 
    HELLO_WORLD: '¡Hola mundo!' 
}, 
'en-GB': { 
    HELLO_WORLD: 'Hello world!'

我们需要翻译我们的字符串对象。我们HELLO_WORLD在每种语言中都有一个带有对应行的变量。对于这种翻译,存在许多语言(例如Java)的MessageFormat格式。让我们尝试弄清楚它是什么。首先,介绍一些基本技术-让我们从Unicode开始。这是为来自不同语言的字符创建单个空格的标准。让我们用国际象棋作一个类比:每种类型的棋子可以具有不同的形状,但我们始终知道它们在棋盘上的确切位置。好吧,当然,存在具有不同字节数的不同Unicode格式:UTF-8,UTF-16和UTF-32。现在,最常用的meta标签是UTF-8。使用Unicode,浏览器可以显示特殊字符(如果使用此标记)`meta忘记了,没有人会明白我们页面上有什么样的符号。

除Unicode外,CLDR和ICU是另外两个重要技术。 CLDR是一种以世界上不同语言存储的字母,国家,货币,时区等数据库。并不是世界上所有的六千种语言都存在,该数据库仍在进行中。最近一次更新是去年10月。另一个重要的项目是ICU。这是一个庞大的数据库,其中包含来自不同语言的单词,数字和符号,这些数据库以排序,规范化,格式化等方法的形式提供。这些库在许多编程语言中均得到使用。在JavaScript中,ICU是Intl API的核心。但是需要在浏览器中显示世界上许多语言的各种材料,以至于将它们包含在这些标准中的工作还远远没有完成。

MessageFormat是一种格式,允许您将特定键与特定语言的特定消息相关联。在某些情况下,可以将变量传递MessageFormat,它定义它们并将其输入到最后一行。在其他语言中,解决相同问题的方式也略有不同。在Android中,MessageFormat是用Java实现的。在那里,使用这种格式,不需要特殊的库; Android能够与其本身进行交互。在iOS中,有一个API与Jav​​aScript中的API非常相似。它内置于系统中,也无需下载任何内容,只需将必要的行传递给此API的方法即可。

如何在JavaScript中解决此问题?还没。但是我们有许多提供解决方案的库。



它显示了其中最受欢迎的下载数量(还有两个不那么流行的下载,分别来自Mozilla的流利和来自Facebook的fbt)。每周有将近200万次下载,因此需要国际化的图书馆。

图书馆


我们将简要介绍其中一些库,并从i18next开始。它的开发人员对MessageFormat进行了许多更改,并且未完全遵循ICU。但是,这是一个非常好的图书馆。它的MessageFormat实现具有许多优点,例如,可以使用字符串插值(在ICU格式中不可用)。但是,它也有缺点,例如,不能像ICU一样将多个消息和唯一的消息放在同一行。

最著名的国际化图书馆之一是intl-messageformat每周下载超过70万次。她的支持由我的同事龙虎(Long Hu)处理。它的受欢迎程度是由在它之上创建react-intl来解释的。因此,如果您使用React,那么很可能您拥有这个库。她的开发人员还参加了ECMA-402,因此他尝试遵守ICU标准。

var MESSAGES = { 
    'en-US': { 
        NUM_PHOTOS: 'You have {numPhotos, plural, ' +
            '=0 {no photos.}' +
            '=1 {one photo.}' +
            'other {# photos.}}'
}, 
    'es-MX': {
        NUM_PHOTOS: 'Usted {numPhotos, plural, ' +
            '=0 {no tiene fotos.}' + 
            '=1 {tiene una foto.}' 
            +'other {tiene # fotos.}}' 
    } 
};

它的实现与MessageFormat非常相似您可以在此处传递变量并指出是否需要复数。

在继续讲代码示例之前,我将讨论另外两个现在流行的新库,它们是由Facebook和Mozilla创建的。



整个API不能整体展示出来,但请注意:这些库的开发人员已尽力而为,这正是我们现在所缺少的。的确,Facebook以自己的风格实现了它:它自己的标记,在布局期间运行的能力,从可以自动翻译的字符串中提取哈希图。问题在于,所有这些都集中在普通程序员很少使用的规模上。该项目还很年轻,他们想将其与其他知名库(例如React)集成。将来,他很可能会受欢迎。

以上所有都是需要额外下载的库,它们没有内置在浏览器中。仅使用一个浏览器,我们就走得太远,因此本地化对所有方面都是不利的。MessageFormat可以帮助我们改变这种状况虽然我们不能使用它,但请相信我:未来就在他身上。现在,我们正在积极努力,建立利益相关者,为新的MessageFormat寻找新的想法在其原始版本中,这种格式已经过时,自开发以来,开发人员的需求已发生了巨大变化。新格式应高质量制作并易于使用。

国际日期时间格式


浏览器已经具有许多内置的国际化和本地化机制,其中大多数根本不了解并且不使用它们。您听说过Intl.DateTimeFormat吗?在这个项目中,我们将不断创建新的API。可能不再需要Moment.jsDay.jsdate- fns

const myDate = new Date(); 
new Intl.DateTimeFormat('ru', { timeStyle : 'short'}).format(myDate); 
// short → 19:49 
// medium → 19:49:17 
// long → 19:49:17 GMT+2
// full → 19:49:17  ,  

有timeStyle,它是几个月前创建的,它允许您格式化日期和时间,而无需使用Moment.js。另外,还有一个formatRange方法与选择日期范围相关的任何任务(例如在具有预订功能的网站上)总是很困难的。但是,此方法已经存在于浏览器中。而且,最重要的是,此方法支持国际化,同时无需下载其他库。

国际相对时间格式


我为该项目的第二部分编写了文档,如果您也想参与其中,我们需要翻译成俄语并符合标准的帮助。当需要倒数计时时,需要RelativeTimeFormat

const myTime = new Intl.RelativeTimeFormat('ru', { style: 'narrow' }); 
myTime.format(2 , 'quarter'); 
//Style Narrow : +2 . → in 2 qtrs. → dentro de 2 trim. 
//Style Long :  2  → in 2 quarters → dentro de 2 trimestres

现在,这样做非常简单,您可以指定两天,两周,四分之一等时间。以前,这种格式在网络上不存在。

const myTime = new Intl.RelativeTimeFormat('ru', { style: 'narrow' }); 
myTime.format(2 , 'day'); 
//Style Narrow : +2 . → in 2 days → dentro de 2 días 
//Style Long :  2  → dentro de 2 días myTime.format(-1 , 'day');
//Style Narrow : -1 . → 1 day ago → hace 1 día 
//Style Long : 1   → 1 day ago → hace 1 día //Numeric(auto) :  → yesterday → ayer 

这是俄语的一个例子。您自己可以测试此代码的操作,因为它已经在您的浏览器中。

const myTime = new Intl.RelativeTimeFormat('ru', { style: 'narrow' }); 
myTime.format(20 , 'seconds'); 
//Style Narrow : +20  → in 20 sec. → dentro de 20 s 
//Style Long :  20  → in 20 seconds → dentro de 20 segundos

该方法非常有用,它可以按照您在上面看到的简短格式给出时间。我强调,对于所有这些,您不需要使用任何第三方库。

国际编号格式


我想分享的下一件事是Intl.NumberFormat我将讨论第三阶段,但是示例中仅介绍了第二阶段,因为仍在讨论一些更改。Intl.NumberFormat适用于单位和记录形式。值得关注他对单元的处理方式:他允许您使用不同的样式。

new Intl.NumberFormat("ru", { 
style: "unit", 
unit: "liter", unitDisplay: "long" 
}).format(16); 
// → 16  → 16 liters → 16 litros

所有单位均取自UTC 35,并且有很多单位。此处总共显示约140个格式化单位。因此,现在国际化比以往任何时候都容易。您只需要翻译线条,浏览器中已经包含了所有必要的动态。

const nbr = 987654321; 
new Intl.NumberFormat('ru', { notation: 'scientific' }).format(nbr); 
// → 9,877E8 → 9.877E8 (en-US) 
new Intl.NumberFormat('ru', { notation: 'engineering' }).format(nbr); 
// → 987,654E6 → 987.654E6 (en-US) 
new Intl.NumberFormat('ru', { notation: 'compact' }).format(nbr); 
// → 988  → 988M (en-US) → 9.9亿 (zn-CN) 
new Intl.NumberFormat('ru', { notation: 'compact', compactDisplay: 'long' }).format(nbr); 
// → 988  → 988 millions (fr)

现在为记录表格。老实说,我不会经常使用它们,因为我不会使用带有指数(科学记录)的录音形式,也不必大量展示。但是,如果您需要它,则有一个专门为您提供的相应API。

国际列表格式


另一个有用的API是Intl.ListFormat,它已经在第三阶段了,它允许您以两种不同的方式来格式化列表。假设我需要说“我要去HolyJS”这句话。我们可以创建一个包含“莫斯科”和“圣 在“ Petersburg”中,指定参数“ joint”,并且这些行将通过俄语“和”的结合进行组合。这是一个全新的功能,非常有用。



如果指定“析取”,则得到联合“或”。



最后,该功能可以自动确定所使用的语言和字母,并相应地对列表项进行排序。

国际规则


另一个重要的API是Intl.PluralRules该API是最古老的,但由于某种原因,没有人使用它。



当我看到比赛或足球比赛的决赛入围者名单时,数字总是显示在名称旁边:“ 1”,“ 2”,“ 3”等。但这与我们所说的更为接近。对于语音,请写下“第一”,“第二”,“第三”。为此,有一些特殊的API,使用起来并不难。



例如,我们可以编写短语“ 1 cat”,“ 0 cats”,“ 0.5 cats”,“ 1.5 cats”,API会自动选择正确的复数结尾。

国际显示名称


这是最流行的API之一,因为我们经常不得不显示国家/地区列表。假设我们有一个国家列表-例如,在数据库或JSON中。然后,每次您切换语言时,我们都需要加载带有国家,货币等新清单的单独的JSON。这些JSON太多了,它如何结束?我们创建了一个微服务,其中内置了具有多种语言的数据库,并从中提取所有数据。当然,在带有国家/地区列表的示例中,我们很幸运,我们需要不经常更新数据-但这并不总是那样,对吗?我们不能一次解决所有问题,但DisplayNames可以解决其中的一些问题。您具有以下示例中的API,并且您只能请求货币列表或国家列表:

const currencyNames = new Intl.DisplayNames(['en'], {type: 'currency'}); currencyNames.of('USD'); // "US Dollar" 
currencyNames.of('EUR'); // "Euro" 
currencyNames.of('TWD'); // "New Taiwan Dollar" 
currencyNames.of('CNY'); // "Chinese Yuan"


const languageNames = new Intl.DisplayNames(['en'], {type: 'language'}); languageNames.of('fr'); // "French" 
languageNames.of('de'); // "German" 
languageNames.of('fr-CA'); // "Canadian French" 
languageNames.of('zh-Hant'); // "Traditional Chinese"

这是非常有用的事情。它不仅适用于国家和货币:以同样的方式,您可以处理开发人员需要的几个月,一周中的几天以及其他许多事情。

未来的结果和计划


到目前为止,我们已经讨论了现有的API。让我们继续我们的未来计划。我的母语是葡萄牙语。因此,在我的网站上,我需要至少支持葡萄牙语和英语。而且由于我们离西班牙非常近,所以西班牙语也很有用。葡萄牙是一个很小的国家,而法国也不是那么遥远,因此将法语添加到此列表中将是很好的。

对我们来说MessageFormat非常相关,它将很快出现。有库,有开发人员在工作。所有这些开发人员都在研究相关问题。大多数最受欢迎的图书馆的创作者和大多数大型公司(Netflix,亚马逊,Facebook)至少在一件事上达成共识:现在迫切需要国际化。每周有200万次下载也表明了这一点。因此,现在我们可以负担得起再次编写MessageFormat并以高质量的方式进行编写

谁将从适当的国际化中受益?整个网络:所有公司,所有项目,所有图书馆。像Intl.MessageFormat这样的不会消失在任何地方,而是会以一种新的方式开始工作。您不需要下载数据,因为所有数据都已经在浏览器中。最有可能的是,您将不需要切换到新库。这些库中的一些已经在某些实现中充当了polyfill。我提到的某些实现是在第三阶段,并非在所有浏览器中都实现。但是像Intl.MessageFormat这样的提供此功能的polyfilling。总的来说,网络历史上的新篇章即将到来-一场真正的革命。网络将变得所有人都可以访问和理解。这是非常重要的。

我认为,确保我们项目的独特性非常重要。如果有一种可以在C ++,Java和JavaScript中使用的格式,那么为什么不在所有地方都使用这种格式?当我们编写网页时,我们经常需要创建它们的移动版本,在这种情况下,我们必须做很多工作两次。如果我们对所有内容都有一种格式,那么我们可以使用现有资源和API。我们需要与工具集成的新高度。国际化不仅由直接参与国际化的开发人员提供。对她来说,模块化非常重要,因为使用自己的格式设置工具和代码通常很方便。因此,您不应该关闭API,它们必须是开放的,以便它们可以连接情况所需。另一个要点:这些API必须是本地的。 CLDR提供国际化API所需的数据。如果您正在运行Windows或MacOS,则您已经在从CLDR下载数据。 CLDR是唯一的存储库;没有人可以复制其功能。这意味着数据只能下载一次并在整个操作系统中通用。如果Intl API的所有数据都已经加载到操作系统中,那么为什么不为该系统上的所有软件提供数据呢?如果Intl API的所有数据已经​​加载到操作系统中,那么为什么不为该系统上的所有软件提供数据呢?如果Intl API的所有数据都已经加载到操作系统中,那么为什么不为该系统上的所有软件提供数据呢?

我们的经验告诉我们要记住,我们并不孤单,不仅程序员致力于国际化。我们是开发人员,而不是翻译人员。假设我们需要在界面中进行换行,然后将它们发送给翻译公司。但是翻译人员通常对这些词没有上下文。在MessageFormat中也没有此功能。有时这会导致错误,正如我们在上述关于俄罗斯火葬场的示例中已经看到的那样。

最后,我相信用于国际化的API应该易于使用,并且每个人都应该能够进行国际化-这不应该花费太多时间和精力。在编写国际化代码时,您需要从一开始就受到指导。毕竟,他们使用TDD首先编写一个测试,然后编写代码。让我们根据正确的国际化和本地化原则开始我们的Web项目。这将使我们能够创建方便所有人访问的站点。
HolyJS 2020 Piter «Speak my language %app%». , , : . HolyJS 2020 Piter . , .

All Articles