Refactor i18n/intl utils, support icu only, remove renderText

This commit is contained in:
Jamie Kyle
2023-06-14 16:26:05 -07:00
committed by GitHub
parent e154d98688
commit b76c7269f8
13 changed files with 361 additions and 6478 deletions

View File

@@ -14,6 +14,7 @@ import { deepEqual } from 'assert';
import type { Rule } from './utils/rule';
import icuPrefix from './rules/icuPrefix';
import wrapEmoji from './rules/wrapEmoji';
import onePlural from './rules/onePlural';
import noLegacyVariables from './rules/noLegacyVariables';
import noNestedChoice from './rules/noNestedChoice';
@@ -24,6 +25,7 @@ import pluralPound from './rules/pluralPound';
const RULES = [
icuPrefix,
wrapEmoji,
noLegacyVariables,
noNestedChoice,
noOffset,
@@ -74,6 +76,26 @@ const tests: Record<string, Test> = {
messageformat: '$a$',
expectErrors: ['noLegacyVariables'],
},
'icu:wrapEmoji:1': {
messageformat: '👩',
expectErrors: ['wrapEmoji'],
},
'icu:wrapEmoji:2': {
messageformat: '<emoji>👩 extra</emoji>',
expectErrors: ['wrapEmoji'],
},
'icu:wrapEmoji:3': {
messageformat: '<emoji>👩👩</emoji>',
expectErrors: ['wrapEmoji'],
},
'icu:wrapEmoji:4': {
messageformat: '<emoji>{emoji}</emoji>',
expectErrors: ['wrapEmoji'],
},
'icu:wrapEmoji:5': {
messageformat: '<emoji>👩</emoji>',
expectErrors: [],
},
};
type Report = {

View File

@@ -0,0 +1,73 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import getEmojiRegex from 'emoji-regex';
import type {
MessageFormatElement,
TagElement,
} from '@formatjs/icu-messageformat-parser';
import {
isTagElement,
isLiteralElement,
} from '@formatjs/icu-messageformat-parser';
import { rule } from '../utils/rule';
function isEmojiTag(
element: MessageFormatElement | null
): element is TagElement {
return element != null && isTagElement(element) && element.value === 'emoji';
}
export default rule('wrapEmoji', context => {
const emojiRegex = getEmojiRegex();
return {
enterTag(element) {
if (!isEmojiTag(element)) {
return;
}
if (element.children.length !== 1) {
// multiple children
context.report(
'Only use a single literal emoji in <emoji> tags with no additional text.',
element.location
);
return;
}
const child = element.children[0];
if (!isLiteralElement(child)) {
// non-literal
context.report(
'Only use a single literal emoji in <emoji> tags with no additional text.',
child.location
);
}
},
enterLiteral(element, parent) {
const match = element.value.match(emojiRegex);
if (match == null) {
// no emoji
return;
}
if (!isEmojiTag(parent)) {
// unwrapped
context.report(
'Use <emoji> to wrap emoji in translation strings.',
element.location
);
return;
}
const emoji = match[0];
if (emoji !== element.value) {
// extra text other than emoji
context.report(
'Only use a single literal emoji in <emoji> tags with no additional text.',
element.location
);
}
},
};
});