引言
作为我项目的一部分,我面临着使公司当前网站使用多种语言的任务。 更准确地说:创建快速,轻松地将网站翻译成英语,波兰语,意大利语等的功能。
在Internet上进行的搜索显示,创建多语言站点的现有选项非常庞大且效率低下。 连接第三方库通常是有问题的,并且编写解决方案的技巧与大量相同类型的工作有关。
编写更改语言环境的替代方法只花了我几个小时的时间,并且在添加新页面时保持语义统一性可以将更改最小化。
带有自动翻译的示例站点的源文件可以
在github上下载
现有替代品
当任务刚刚出现在开发中时,第一步当然是在论坛上研究现成的解决方案和技巧,以了解如何最轻松,正确地实现更改语言环境的可能性。 用于创建多语言站点的最受欢迎的Internet资源提供以下解决方案:
- 用不同语言的文本创建重复的html块,其中只有一个对用户有效,其余的则被隐藏(显示:无)。
这种方法的明显缺点是代码的增加非常快,并且立即丧失了代码的可读性和可维护性。 另外,就增加语言(以下称为语言环境)的数量而言,该解决方案容易受到文本错误和可伸缩性的影响。
- 连接具有多种内置语言的第三方机器翻译服务(例如Google翻译),而对页面源代码的更改却很少。
当任务首次出现在任务列表中时,我们使用此方法是最明显,最方便的方法,但是,与来自美国和以色列的母语人士合作的经验表明,机器翻译在更改语言环境时经常会犯错误,并且站点用户对此非常敏感。翻译错误。 最后,战略合作伙伴强烈建议更改更改语言环境的方法,因此必须放弃此方法。
- 根据DOM元素的搜索和直接更改,使用js或第三方库/框架(如jQuery)的功能来更改语言。
这种方法的一个特点是搜索大量的js选择器,其中的文本必须替换。 这种方法可能适用于小型项目,但是随着页面数量的增加,文本替换功能的数量也成比例增加,这导致大型项目的效率下降。
替代解决方案
奇怪的是,我提议的方法的基础不是由我自己编写的js代码(通常是微不足道的),也不是由我自己编写的基础,选择器的设计使您能够灵活,轻松地将任意数量的页面翻译成任何语言,而无需任何操作代码库更改和过多的数据重复。
在使用其他方法更改语言环境时,有三个主要参与者:
- 具有文本块选择器已建立规则的html页面
- 常规js服务,其主要任务是根据选择器设计规则替换textContet DOM元素
- 区域设置JSON文件,该文件包含一个结构,该结构包含更改语言环境时使用的所有语言的html块
符合可变元素选择器的设计规则,因此无需更改语言环境更改服务的js代码,这对项目可伸缩性而言是一大优势。
构造选择器的规则
大多数更改页面语言环境的方法(在替代方法1.3和2部分中)建议您需要以某种方式“标记”变量html块,因为通过更改类字段是正确的。 相同的机制使用替代选项。
设计选择器的第一步是将源页面分为顶级功能块。 在我们公司的页面上有块:
我们给每个块一个条件名称,例如,
菜单
名片 (首页)
服务操作示例(示例)
合作伙伴 (客户)
服务范围 (userfulBlock)
服务示例(示例)
联系人和反馈 (联系人)
之后,我们进一步将每个模块分解为较小的功能模块,就像使用React库一样。
我们将名称分配给所选区域,并获得以下形式的结构:
继续此过程,直到到达包含源文本的块。
结果,我们得到了一个现成的语言环境JSON文件结构,其中包含用于更改语言的所有必要文本。 同样,基于此算法,确定构造选择器的规则:
每个选择器均以locale关键字开头,然后根据破折号样式,添加所有父块的名称,包括包含源文本的块,例如,第一张卡片中的示例说明将具有locale-example-cards-description选择器
可以
在github上看到生成的语言环境json文件的示例
区域变更服务
语言环境更改服务是包含加载语言环境文件功能的模块
loadLocale(defLang)
使用可选的defLang参数-加载语言环境后设置的语言(默认语言),以及更改当前语言环境的主要功能
changeLocale(lang)
指示所需的语言。
语言环境下载功能
语言环境加载功能将标准XMLHttpRequest请求用于数据。 该标准的使用是由于希望最大程度地减少依赖项的数量以及简化请求的使用。 接收到语言环境文件后,控制台中将显示有关接收数据的通知,并且如果将该语言环境作为可选参数传递给该函数,则会调用将语言环境更改为默认语言的功能。 您可以在此处熟悉功能代码:
function loadLocale(defLang) { var xhr = new XMLHttpRequest(); xhr.open("GET", 'http://localhost:3000/locale.json', true); xhr.onreadystatechange = saveLocale.bind(this); xhr.onerror = function () { console.log("no found page"); }; xhr.send(); function saveLocale() { if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) { locale = JSON.parse(xhr.responseText); console.log("locale loaded"); if(defLang) changeLocale(defLang); } } }
区域变更功能
资料类型
它是一个递归函数,其主要任务是遍历包含页面区域设置的对象(使用DFS算法)。 在构造函数时使用递归可以使您尽可能简单简洁地对算法进行编码,但是,过多的递归深度会导致堆栈溢出。 可以在同名论坛上找到解决此问题的功能,或者通过阅读habr.com上的相关文章。
递归函数基于以下4种数据类型的处理:
- 包含用于添加到页面的源文本字符串的字段。
例如:
"main": " "
- 包含用于添加到的源文本行数组的字段
页面。 对于创建可更改其元素的列表,此字段是必需的。
订单。 例如:
"menu":["Home","Example","Clients","Info","Contacts"]
- 嵌套数据结构包含构建所需的自己的字段集
页面架构。 例如:
"home": { "main": "selling quest from your video", "description": "for social networks & sites", "buttons": ["try","order"] }
- 嵌套数据结构的数组,使用相同的字段集。 这样的
当出现相同代码块列表时,使用数组,例如,
团队成员卡,所提供服务的投资组合或资费。
例如:
"usefulCards": [ { "headline": "Marketers and agencies", "statistics": ["convers 26%", "retent 25%"], "button": "ORDER" }, { "headline": "Production studios and TV platforms", "statistics": ["convers 24%", "retent 33%"], "button": "ORDER" }, { "headline": "Conference creators", "statistics": ["convers 65%", "retent 15%"], "button": "ORDER" }, { "headline": "Bloggers and streamers", "statistics": ["convers 24%", "retent 33%"], "button": "ORDER" } ],
在站点上,它可能看起来像这样:
处理功能
源数据类型处理由单独的功能执行
function getText(key, object, name,startIndex)
具有源文本的结构字段的输入字段名称,包含要添加的文本的当前语言环境对象以及搜索DOM元素所需的当前选择器名称。
function getText(key, object, name, startIndex) { var elementKey=0; if(startIndex) elementKey = startIndex; for ( ; elementKey < document.getElementsByClassName(name + "-" + key).length; elementKey++) if (!isNaN(elementKey)) document.getElementsByClassName(name + "-" + key)[elementKey].textContent = object[key]; }
还可以通过单独的函数来处理带有源文本的字符串数组
function getArrayText(key, object, name,startIndex)
该函数的签名和主体与过去没有什么不同,只是将数组中的元素分配给DOM元素。
function getArrayText(key, object, name, startIndex) { var elementKey=0; if(startIndex) elementKey = startIndex; for ( ; elementKey < document.getElementsByClassName(name + "-" + key).length; elementKey++) if (!isNaN(elementKey)) document.getElementsByClassName(name + "-" + key)[elementKey].textContent = object[key][elementKey % object[key].length]; }
用于替换文本的主要递归函数涉及将当前语言环境字段分类为上述4种类型之一,并对结果类型进行相应的反应:
function changeText(name, object, startIndex) { for (key in object) if (Array.isArray(object[key]) && typeof object[key] != 'string' && typeof object[key][0] == 'string') getArrayText(key, object, name); else if (typeof object[key] == "object" ){ if(isNaN(key)) changeText(name + "-" + key, object[key]); else changeText(name, object[key],key); } else getText(key, object, name, startIndex); }
此函数接受当前语言的语言环境和根选择器(在本例中为“语言环境”)。 此外,在检测到嵌套结构或结构数组时,该函数将递归调用自身,相应地更改输入参数。
替代方法的主要优点是,上述服务不需要任何功能更改,并使用您创建的语言环境文件作为js文件添加。
结论
上述方法的本质是用于描述选择器和构建语言环境文件的固定规则。 有了这个,就有了一个难得的机会来翻译任何现成的页面并重新使用已经翻译的材料。
上面描述的用于构造选择器的算法对于服务不是强制性的,也不是至关重要的。 该服务可以灵活地扩展和添加新的方法和算法,以及构造选择器名称和json语言环境结构。 一个可能的好处是,根据服务用户的位置,将语言环境保存在浏览器cookie中并更改语言环境。
带有自动翻译的示例站点的源文件可以
在github上下载。