美好的一天,habravchane!
在本文中,我将讨论一个简单的解决方案,用于解决Web应用程序中的文本和本地化问题,您可以实现自己或使用现成的解决方案。

我长期以来一直想分享自己的想法和经验,当然,也可以终身交流。
背景知识显然,已经存在用于管理文本和本地化的解决方案,但是由于种种原因它们并不适合我:笨拙,使用不便,不合适,不符合我解决这个问题的愿景,缺乏功能。
另外,由于第三方库的增长趋势,我并不十分喜欢它(这是当我们只需要一小部分所有功能时)。
我工作的公司有自己的解决方案,但我认为这也不理想。 与旧版本的向后兼容性的需求使其变得不必要地复杂。
在某个时候,我想要一些简单,容易,可理解并且可以无限扩展以完成不同任务的东西。
问题陈述
这里的一切似乎都很清楚。 还是不行 让我们考虑一下我们想要什么。
我们需要以某种方式获取本地化文本。 文本可能包含变量。 变量也可以本地化吗? 从理论上讲,是的。 如果变量是日期或数字? 加上降价支持。 最后,一些解决方案,以防找不到文本。
实作
基础将是一个简单的对象,其中的键是文本代码,而值是您需要的实际文本,没有什么复杂的:
const textsBundle = { 'button.open': 'Open', 'button.save': 'Save', }; function TextManager(texts) { this.getText = function(code) { return texts[code]; }; } const textManager = new TextManager(textsBundle); textManager.getText('button.open');
关于按键名称的一点点键的名称是一个单独的主题。 最好立即就一个选项达成共识,否则不同的键将“搅动” :)。 没有一种解决方案,选择您认为更方便且与项目更一致的内容。 就个人而言,我喜欢第一个:
'button.open.label'
'button.open.help_text'
或
'button.label.open'
'button.help_text.open'
或
'label.button.open'
'help_text.button.open'
接下来,我们需要一种机制,该机制能够在给出最终结果之前对文本执行某种操作,例如,插入参数。 然后我有了一个有趣的想法-如果我们使用中间件来操纵文本怎么办? 毕竟,我还没有看到过这样的决定……好吧,或者我看上去很糟:)。
我们决定对中间件的要求:在必要的操作之后,中间件将在输入时接受文本和参数,并给出结果文本。
第一个中间件将接收原始文本,随后的中间件将接收前一个中间件的文本。 让我们添加缺少的代码:
function TextManager(texts, middleware) { function applyMiddleware(text, parameters, code) { if (!middleware) return text; return middleware.reduce((prevText, middlewareItem) => middlewareItem(prevText, parameters, code), text); } this.getText = function(code, parameters) { return applyMiddleware(texts[code], parameters, code); }; }
TextManager可以通过其代码输出文本。 还可以使用中间件对其进行扩展,这带来了许多可能性,例如:
- 处理找不到文本的情况
- 在文本中使用参数
- 参数本地化
- 使用降价
- 文字屏蔽等
练习
我们将编写一些必要的中间件。 您将需要100%。
插入参数
允许在文本中使用参数。 例如,我们需要显示文本“ Hello {{username}}”。 以下中间件将提供此功能:
function InsertParams(text, parameters) { if (!text) return text; if (!parameters) return text; let nextText = text; for (let key in parameters) { if (parameters.hasOwnProperty(key)) { nextText = text.replace('{{' + key + '}}', parameters[key]); } } return nextText; }
UseCodeIfNoText
如果找不到文本,则允许您返回文本代码,而不是undefined
:
function UseCodeIfNoText(text, parameters, code) { return text ? text : code; }
总计,我们获得以下用途:
const textsBundle = { 'text.hello': 'Hello', 'text.hello_with_numeric_parameter': 'Hello {{0}}', 'text.hello_with_named_parameter': 'Hello {{username}}', }; const textManager = new TextManager(textsBundle, [InsertParams, UseCodeIfNoText]); textManager.getText('nonexistent.code')
React应用示例
首先,在顶级TextManager
初始化并添加文本。
我认为,最好从服务器中提取文本,但是为了简单起见,我不会这样做。
const textsBundle = { 'text.hello': 'Hello {{username}}' } function TextManagerProvider({ children }) { const textManager = new TextManager(textsBundle, [InsertParams, UseCodeIfNoText]); return ( <TextManagerContext.Provider value={textManager}> {children} </TextManagerContext.Provider> ) }
接下来,在组件中, textManager
使用textManager
,例如,使用一个钩子,然后通过代码获得所需的文本。
function SayHello({ username }) { const textManager = useContext(TextManagerContext); return ( <div> {textManager.getText('text.hello', { username })} </div> ) }
本地化
您会问:“本地化与它有什么关系?”。
一切都很简单-更改语言时,创建TextManager
的新实例,添加文本并立即获得结果。
倒数第二章:)
从示例中可以看到,使用非常简单,而且由于有了中间件,您可以无限扩展功能。
我将实现发布在github上,并计划进一步开发text-manager 。 使用并提供改进,就如他们所说,不客气! :)
总结
因此,我实现了我的daaaaavnee愿望-在Habr上写了一篇文章。 我真的希望这篇文章对您有所帮助,并能使社区满意。
谢谢您的关注。