未加星标

Roll Your Own JavaScript i18n Library with TypeScript Part 2

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

Inpart 1 of this series, we observed several reasons why we might want to roll our ownjavascript i18n library. We alsotook the first steps into building such a library. Let’s quickly revisit this.

Writing our own i18n library offers us two crucial benefits:

lean code that gives us exactly what our app needs, saving kilobytes downloaded and code parsing time in the browser; a good amount of insight into how i18n libraries work, which deepens our understanding of i18n as developers.

So what would an in-house i18n library have to do?

Basic Functionality (refer topart 1for all details)

Locale detection and resolution Defining supported locales Translation file loading for the resolved locale Manually setting/forcing the locale Displaying translations (retrieval from the currently loaded translation file) Interpolation (what you’re just about to read) Handling dynamic arguments in our translation strings Handling singular/plural forms Formatting dates Formatting currency

At this point, we have the library’s basic functionality done, and we’re ready to tackle interpolation. If you missed the first part, I urge you tocheck it out, as the code in this second part builds on top of what we put in place there. You can find the code that goes along with the first part on Github . Let’s very briefly go over what this code does.

Note: The code for this part, part 2, is on Github as well.

Our JavaScript i18n Library so Far

We’ve called our library Gaia. Here’s a look at Gaia’s API as it stands right now.

Note: We’re using TypeScript to build our library. We go over our reasoning for using TypeScript in part 1. If you’re a fan of TypeScript, we got you covered. If you just want plain old JavaScript, feel free to ignore the type information in this article’s code. TypeScript, after all, is effectively JavaScript with strong typing.

/** * Initialize the library. * * @param options.supportedLocales - locales the app supports * @param options.locale - initial locale, defaults to the browser locale * @param options.fallbackLocale - locale to use if `options.locale` is not *provided and we cannot determine the browser locale * @returns Promise<string> - resolves with the determined locale after loading the locale's translation file */ gaia.init(options: { supportedLocales: string[], locale?: string, fallbackLocale?: string, }): Promise<string> /** * Normalized array of supported locales * e.g. `['en-us', 'ar', 'fr']`. */ gaia.supportedLocales: ReadonlyArray<string> /** * Check if the given locale is supported. * * @param locale - the locale to check */ gaia.isSupported(locale: string): boolean /** * The current locale e.g. 'en-us' */ gaia.locale: string /** * Set the current locale and load its translations. * * @param locale - the locale to set */ gaia.setLocale(locale: string): Promise<void> /** * Retrieve a string translated to the current locale. * * @param key - index of translation to retrieve * @returns string */ gaia.t(key: string): string

Our library will assume that translation files for supported locales are provided as JSON that is available publicly at the URI /lang/<locale>.json and are simple key-value string pairs.

/lang/fr.json

{ "title": "Votre panier", "lead": "Bonsoir, Adam. Voici ce qui se trouve actuellement...", // ... }

These translations are used when a localized string is retrieved by the t function.

This allows us to do something like this in our app:

const supportedLocales = { 'ar-eg': '', fr: 'Franais', en: 'English' }; // on load gaia .init({ supportedLocales, locale: 'fr' }) .then((locale) => { // translations are ready to use, proceed to rendering }); // on render / display h1Element.innerHTML = gaia.t('title'); // renders title in French Simple Interpolation

This works fine for static strings, but we’ll need to build on our solution for dynamic values. Let’s assume that our user’s name is dynamic and that we don’t want to hard-code it in our translation files. We could provide it as an argument to our t function.

pElement.innerHTML = t('lead', { name: 'Adam' });

In our translation files, we can specify a placeholder for this argument.

{ "lead": "Bonsoir, {name}. Voici ce qui se trouve..." }

What the user would see, of course, would be: “Bonsoir, Adam. Voici ce qui se trouve…”

So we’ll accept a plain old JavaScript object for our arguments, and use the {placeholder} syntax in our language files. Let’s implement the logic that makes this work.

src/gaia/gaia.ts

//... import { interpolateSimple } from './lib/interpolate'; // ... import { Translations, TranslationReplacements } from './lib/types'; // ... const gaia = { // ... t(key: string, replacements?: TranslationReplacements): string { const translated = _translations[key] || key; if (replacements === undefined || Object.keys(replacements).length === 0) { return translated; } return interpolateSimple(translated, replacements); }, }; // ...

src/gaia/lib/types.ts

export interface StringToStringMap { [key: string]: string } export interface Translations { [key: string]: string } export interface TranslationReplacements { [key: string]: string }

Note: You can get all of this article’s code on Github .

Note: Recall that _translations is a string to string map that contains the key-value pairs from the currently loaded translation file. We go over this inpart 1.

We accept an optional replacements parameter in our t function. If we are given a replacements map, we run our interpolation logic on it via a utility function, interpolateSimple . We will handle other kinds of interpolations ― namely plurals, dates, and currency ― in the future; so we establish a naming convention for our interpolation helpers. Let’s take a look at our simple interpolator, which swaps given replacements for placeholders.

src/gaia/lib/interpolate.ts

import { TranslationReplacements } from "./types"; export function interpolateSimple( source: string, replacements: TranslationReplacements, ): string { const placeholderRegexp = getPlaceholderRegexp(); let interpolated = source; let currentMatch = placeholderRegexp.exec(source); while (currentMatch !== null) { const [placeholder, replacementKey] = currentMatch; const replacement = replacements[replacementKey.trim()]; if (replacement !== undefined) { interpolated = interpolated.replace(placeholder, replacement.toString()); } currentMatch = placeholderRegexp.exec(source); } return interpolated; } let _placeholderRegExp: RegExp; function getPlaceholderRegexp(): RegExp { if (_placeholderRegExp === undefined) { _placeholderRegExp = new

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

代码区博客精选文章
分页:12
转载请注明
本文标题:Roll Your Own JavaScript i18n Library with TypeScript Part 2
本站链接:https://www.codesec.net/view/621236.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(21)