ts: Convert i18n module to TypeScript.

We update our tsconfig target to ES2020 so that we don't get type
errors related to some properties not being in Intl that is used by
@formatjs/intl.
This commit is contained in:
Priyank Patel 2021-05-27 18:08:50 +00:00 committed by Tim Abbott
parent f1a0a790fe
commit 9418ae51fa
3 changed files with 37 additions and 14 deletions

View File

@ -1,7 +1,9 @@
// For documentation on i18n in Zulip, see: // For documentation on i18n in Zulip, see:
// https://zulip.readthedocs.io/en/latest/translating/internationalization.html // https://zulip.readthedocs.io/en/latest/translating/internationalization.html
import type {MessageDescriptor} from "@formatjs/intl";
import {DEFAULT_INTL_CONFIG, IntlErrorCode, createIntl, createIntlCache} from "@formatjs/intl"; import {DEFAULT_INTL_CONFIG, IntlErrorCode, createIntl, createIntlCache} from "@formatjs/intl";
import type {FormatXMLElementFn, PrimitiveType} from "intl-messageformat";
import _ from "lodash"; import _ from "lodash";
import {page_params} from "./page_params"; import {page_params} from "./page_params";
@ -28,26 +30,29 @@ export const $t = intl.formatMessage;
export const default_html_elements = Object.fromEntries( export const default_html_elements = Object.fromEntries(
["b", "code", "em", "i", "kbd", "p", "strong"].map((tag) => [ ["b", "code", "em", "i", "kbd", "p", "strong"].map((tag) => [
tag, tag,
(content_html) => `<${tag}>${content_html}</${tag}>`, (content_html: string) => `<${tag}>${content_html}</${tag}>`,
]), ]),
); );
export function $t_html(descriptor, values) { export function $t_html(
descriptor: MessageDescriptor,
values?: Record<string, PrimitiveType | FormatXMLElementFn<string, string>>,
): string {
return intl.formatMessage(descriptor, { return intl.formatMessage(descriptor, {
...default_html_elements, ...default_html_elements,
...Object.fromEntries( ...Object.fromEntries(
Object.entries(values ?? {}).map(([key, value]) => [ Object.entries(values ?? {}).map(([key, value]) => [
key, key,
typeof value === "function" ? value : _.escape(value), typeof value === "function" ? value : _.escape(value?.toString()),
]), ]),
), ),
}); });
} }
export let language_list; export let language_list: typeof page_params["language_list"];
export function get_language_name(language_code) { export function get_language_name(language_code: string): string {
const language_list_map = {}; const language_list_map: Record<string, string> = {};
// One-to-one mapping from code to name for all languages // One-to-one mapping from code to name for all languages
for (const language of language_list) { for (const language of language_list) {
@ -56,7 +61,7 @@ export function get_language_name(language_code) {
return language_list_map[language_code]; return language_list_map[language_code];
} }
export function initialize(language_params) { export function initialize(language_params: {language_list: typeof language_list}): void {
const language_list_raw = language_params.language_list; const language_list_raw = language_params.language_list;
// Limit offered languages to options with percentage translation >= 5% // Limit offered languages to options with percentage translation >= 5%
@ -75,13 +80,22 @@ export function initialize(language_params) {
// This formats language data for the language selection modal in a // This formats language data for the language selection modal in a
// 2-column format. // 2-column format.
export function get_language_list_columns(default_language) { type LanguageListColumn = {
const formatted_list = []; [prop in "first" | "second"]?: {
code: string;
name: string;
name_with_percent: string;
selected: boolean;
};
};
export function get_language_list_columns(default_language: string): LanguageListColumn[] {
const formatted_list: LanguageListColumn[] = [];
const language_len = language_list.length; const language_len = language_list.length;
const firsts_end = Math.floor(language_len / 2) + (language_len % 2); const firsts_end = Math.floor(language_len / 2) + (language_len % 2);
const firsts = _.range(0, firsts_end); const firsts = _.range(0, firsts_end);
const seconds = _.range(firsts_end, language_len); const seconds = _.range(firsts_end, language_len);
const longest_zip = []; const longest_zip: [number, number][] = [];
// Create a zip (itertool.zip_longest in python) // Create a zip (itertool.zip_longest in python)
for (const value of firsts) { for (const value of firsts) {
@ -89,11 +103,11 @@ export function get_language_list_columns(default_language) {
} }
for (const row of longest_zip) { for (const row of longest_zip) {
const item = {}; const item: LanguageListColumn = {};
const zip_row = [ const zip_row = [
["first", row[0]], ["first", row[0]],
["second", row[1]], ["second", row[1]],
]; ] as const;
for (const zip_value of zip_row) { for (const zip_value of zip_row) {
if (zip_value[1] !== undefined) { if (zip_value[1] !== undefined) {
const lang = language_list[zip_value[1]]; const lang = language_list[zip_value[1]];

View File

@ -1,7 +1,16 @@
import $ from "jquery"; import $ from "jquery";
const t1 = performance.now(); const t1 = performance.now();
export const page_params: Record<string, unknown> = $("#page-params").remove().data("params"); export const page_params: {
language_list: {
code: string;
locale: string;
name: string;
percent_translated: number | undefined;
}[];
request_language: string;
translation_data: Record<string, string>;
} = $("#page-params").remove().data("params");
const t2 = performance.now(); const t2 = performance.now();
window.page_params_parse_time = t2 - t1; window.page_params_parse_time = t2 - t1;
if (!page_params) { if (!page_params) {

View File

@ -14,7 +14,7 @@
/* Basic options */ /* Basic options */
"noEmit": true, "noEmit": true,
"target": "ES2019", "target": "ES2020",
"esModuleInterop": true, "esModuleInterop": true,
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,