Skip to content

Commit

Permalink
feat(bin): add support for extracting keys and values from expression…
Browse files Browse the repository at this point in the history
…s like t.t(key, text)
  • Loading branch information
eyelly-wu committed May 14, 2024
1 parent a9a5813 commit 554cb78
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 48 deletions.
185 changes: 151 additions & 34 deletions src/bin/extra-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,132 @@ import chalk from './chalk'
const fs = require('fs')

/**
* 基于文本解析出tr函数包裹的内容
* 验证文案是否有效
* @param label 包含 `'" 符号的完整整文本
* @param content 符号内的文本内容
* @returns
*/
function validateLabelAndContent(label: string, content: string) {
if (
(label.match(/^`.*`$/) && label.includes('${')) ||
content.startsWith(' ') ||
content.endsWith(' ') ||
content.includes('\n') ||
content.includes('\\n') ||
content.includes('\t') ||
content.includes('\\t')
)
return false

return true
}

/**
* 基于文本解析出 t 函数包裹的内容
* @param fileContent
* @param funcName 获取国际化文本的函数名
* @param success 正确的列表
* @param error 错误的列表
*/
export function extraText(
export function extraTextFromT(
fileContent: string,
funcName,
): {
success: string[] // 正确的列表
error: string[] // 错误的列表
} {
funcName: string,
success: string[],
error: string[],
) {
const regexp = new RegExp(
/\WfuncName\(\n*[ ]*((['"`])(.+?)\2)(,|\))/.source.replace(
'funcName',
funcName,
),
'g',
)
const success: string[] = []
const error: string[] = []
let temp: string[] | null

while ((temp = regexp.exec(fileContent))) {
const label = temp[1]
const content = temp[3]
if (
(label.match(/^`.*`$/) && label.includes('${')) ||
content.startsWith(' ') ||
content.endsWith(' ') ||
content.includes('\n') ||
content.includes('\\n') ||
content.includes('\t') ||
content.includes('\\t')
) {
if (!validateLabelAndContent(label, content)) {
error.push(content)
} else {
success.push(content)
}
}
}

return {
success,
error,
/**
* 基于文本解析出 t.t 函数包裹的内容
* @param fileContent
* @param funcName 获取国际化文本的函数名
* @param keyTextMap 自定义key与文案的映射(需要为一对一)
* @param textKeyMap 文案与自定义key与的映射(可以是一对多)
* @param textSuccess 正确的文案列表
* @param textError 错误的文案列表
* @param keySuccess 正确的自定义key列表
* @param keyError 错误的自定义key列表
*/
export function extraTextFromTDotT(
fileContent: string,
funcName,
keyTextMap: Record<string, string>,
textKeyMap: Record<string, string[]>,
textSuccess: string[],
textError: string[],
keySuccess: string[],
keyError: string[],
) {
const regexp = new RegExp(
/\WfuncName\.t\(\n*[ ]*((['"`])(.+?)\2),\n*[ ]*((['"`])(.+?)\5)(,|\))/.source.replace(
'funcName',
funcName,
),
'g',
)

let temp: string[] | null

while ((temp = regexp.exec(fileContent))) {
const keyLabel = temp[1]
const keyContent = temp[3]
const textLabel = temp[4]
const textContent = temp[6]

const keyValidate = validateLabelAndContent(keyLabel, keyContent)
const textValidate = validateLabelAndContent(textLabel, textContent)

if (keyValidate) {
keySuccess.push(keyContent)
} else {
keyError.push(keyContent)
}
if (textValidate) {
textSuccess.push(textContent)
} else {
textError.push(textLabel)
}

// 自定义key和文案都有效
if (keyValidate && textValidate) {
const text = keyTextMap[keyContent]
if (text && text != text) {
textError.push(
t(
'当前自定义key={0},配置了不一样的文案({1}和{2}),应该满足key与文案一对一的关系',
keyContent,
text,
textContent,
),
)
} else if (!text) {
keyTextMap[keyContent] = textContent
}

const keys = textKeyMap[textContent] || []
if (!keys.includes(keyContent)) {
keys.push(keyContent)
textKeyMap[textContent] = keys
}
}
}
}

Expand All @@ -59,28 +143,61 @@ export default function extraTexts(
filepaths: string[],
funcName = 't',
): {
success: string[] // 正确的列表
error: string[] // 错误的列表
textSuccess: string[] // 正确的列表
textError: string[] // 错误的列表
keySuccess: string[] // 正确的列表
keyError: string[] // 错误的列表
} {
let success: string[] = []
let error: string[] = []
let textSuccess: string[] = []
let textError: string[] = []
let keySuccess: string[] = []
let keyError: string[] = []
const keyTextMap: Record<string, string> = {}
const textKeyMap: Record<string, string[]> = {}

filepaths.forEach((filepath) => {
const fileContent = fs.readFileSync(filepath, {
encoding: 'utf-8',
})
const trTextRes = extraText(fileContent, funcName)
success.push(...trTextRes.success)
error.push(...trTextRes.error)
extraTextFromT(fileContent, funcName, textSuccess, textError)
extraTextFromTDotT(
fileContent,
funcName,
keyTextMap,
textKeyMap,
textSuccess,
textError,
keySuccess,
keyError,
)
})

success = Array.from(new Set(success))
error = Array.from(new Set(error))
textSuccess = Array.from(new Set(textSuccess))
textError = Array.from(new Set(textError))
keySuccess = Array.from(new Set(keySuccess))
keyError = Array.from(new Set(keyError))

logSuccess(chalk.greenBright(t('解析符合要求的翻译文案数:')), success.length)
logSuccess(chalk.greenBright(t('解析不符合要求的翻译文案数:')), error.length)
logSuccess(
chalk.greenBright(t('解析符合要求的翻译文案数:')),
textSuccess.length,
)
logSuccess(
chalk.greenBright(t('解析不符合要求的翻译文案数:')),
textError.length,
)
logSuccess(
chalk.greenBright(t('解析符合要求的自定义key数:')),
keySuccess.length,
)
logSuccess(
chalk.greenBright(t('解析不符合要求的自定义key数:')),
keyError.length,
)

return {
success,
error,
textSuccess,
textError,
keySuccess,
keyError,
}
}
10 changes: 5 additions & 5 deletions src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async function translateController({
})

const translateRes = await translateTextsToLangsImpl(
trTextRes.success,
trTextRes.textSuccess,
sourceLangs,
incrementalMode,
)
Expand All @@ -101,20 +101,20 @@ async function translateController({

writeFilesSync({
filepath: path.join(outputPath, logDirname, 'texts.json'),
fileContent: trTextRes.success,
fileContent: trTextRes.textSuccess,
showName: t(
'提取的翻译文案({0})',
chalk.greenBright(trTextRes.success.length),
chalk.greenBright(trTextRes.textSuccess.length),
),
indentSize,
})

writeFilesSync({
filepath: path.join(outputPath, logDirname, 'texts-error.json'),
fileContent: trTextRes.error,
fileContent: trTextRes.textError,
showName: t(
'提取的编写不规范的翻译文案({0})',
chalk.redBright(trTextRes.error.length),
chalk.redBright(trTextRes.textError.length),
),
indentSize,
})
Expand Down
26 changes: 17 additions & 9 deletions test/bin/extra-text.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { readFileSync } from 'fs'
import { join } from 'path'
import { binExtraText } from '../utils'

const { extraText } = binExtraText
const { extraTextFromT } = binExtraText

describe('验证翻译文本提取功能', () => {
const content = readFileSync(join(__dirname, '../i18n/text.ts'), {
Expand All @@ -28,25 +28,33 @@ describe('验证翻译文本提取功能', () => {

describe('验证规范文本提取', () => {
it('默认提取', () => {
const texts = extraText(content, 't')
expect(texts.success).toEqual(successTexts)
const success = []
const error = []
extraTextFromT(content, 't', success, error)
expect(success).toEqual(successTexts)
})

it('自定义函数名(t -> i18n)', () => {
const texts = extraText(content.replaceAll('t(', 'i18n('), 'i18n')
expect(texts.success).toEqual(successTexts)
const success = []
const error = []
extraTextFromT(content.replaceAll('t(', 'i18n('), 'i18n', success, error)
expect(success).toEqual(successTexts)
})
})

describe('验证不规范文本提取', () => {
it('默认提取', () => {
const texts = extraText(content, 't')
expect(texts.error[1]).toBe(errrorTexts[1])
const success = []
const error = []
const texts = extraTextFromT(content, 't', success, error)
expect(error[1]).toBe(errrorTexts[1])
})

it('未知匹配函数名i18n', () => {
const texts = extraText(content, 'i18n')
expect(texts.success).toEqual([])
const success = []
const error = []
const texts = extraTextFromT(content, 'i18n', success, error)
expect(success).toEqual([])
})
})
})

0 comments on commit 554cb78

Please sign in to comment.