All files / src/utils i18n.js

14.28% Statements 11/77
11.23% Branches 10/89
25% Functions 3/12
14.28% Lines 9/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121  1x 1x                                                 13x 13x 11x 13x       8x 8x           8x                                                                                                                                                              
export function validateModuleCode(code) {
  const value = String(code ?? '').trim()
  Eif (!value) return '编码不能为空'
  if (value.length < 2 || value.length > 50) return '编码长度需为 2-50 个字符'
  if (!/^[a-z][a-z0-9_]*$/.test(value)) {
    return '编码须以小写字母开头,且只能包含小写字母、数字和下划线'
  }
  return ''
}
 
export function getI18nNamespace(key) {
  return String(key || '').split('.', 1)[0] || 'common'
}
 
export function isValidI18nKey(key) {
  return /^[A-Za-z][A-Za-z0-9_.:-]{1,149}$/.test(String(key || ''))
}
 
export function buildModuleItemI18nKey(prefix, moduleCode, itemCode) {
  const normalizedModuleCode = String(moduleCode || '').trim()
  const normalizedItemCode = String(itemCode || '').trim()
  return normalizedModuleCode && normalizedItemCode
    ? `${prefix}.${normalizedModuleCode}.${normalizedItemCode}`
    : null
}
 
export function buildModuleI18nKey(moduleType, moduleCode) {
  const normalizedModuleCode = String(moduleCode || '').trim()
  if (!normalizedModuleCode) return null
  const prefix = moduleType === 'data' || moduleType === 'query' ? 'module' : 'menu'
  return `${prefix}.${normalizedModuleCode}`
}
 
export function syncModuleItemI18nKeys(rows, prefix, moduleCode, codeField) {
  let changed = false
  for (const row of rows || []) {
    const key = buildModuleItemI18nKey(prefix, moduleCode, row?.[codeField])
    if (row.b_i18n === key) continue
    row.b_i18n = key
    changed = true
  }
  return changed
}
 
export function groupI18nRows(rows) {
  const groups = {}
  rows.forEach((item) => {
    const key = item.b_key
    groups[key] ||= { key, values: {}, records: {} }
    groups[key].values[item.b_locale] = item.b_value
    groups[key].records[item.b_locale] = item
  })
  return groups
}
 
// 将同步字段生成的默认翻译条目并入透视行数组:
// - 已存在同 b_key 的行则补全其空语言值(不覆盖用户已填内容);
// - 否则新增一行;managedId 用于关联字段,使重命名字段时复用同一行(改名而非新增)。
// 返回新的数组,不修改入参。
export function mergeI18nDefaults(rows, entries) {
  const next = [...rows]
  const byKey = new Map(next.map((r) => [String(r.b_key), r]))
  const managedIds = new Set(
    next
      .map((r) => r._managedId)
      .filter(Boolean)
      .map(String),
  )
  for (const e of entries || []) {
    if (!e.key) continue
    const existing = byKey.get(String(e.key))
    if (existing) {
      // 已存在同键行:仅补全空值,保留用户已配置的翻译
      if (existing[e.locale] == null || existing[e.locale] === '') existing[e.locale] = e.value
      continue
    }
    // 同键不存在,但该受管键已由其它行代表时不再新建,避免重复键
    if (e.managedId && managedIds.has(String(e.managedId))) continue
    const row = {
      _id: 'i18n_' + Math.random().toString(36).slice(2, 10),
      b_key: e.key,
      _localeIds: {},
      _managedId: e.managedId ? String(e.managedId) : undefined,
      _syncInjected: e.managedId ? true : undefined,
      [e.locale]: e.value,
    }
    next.push(row)
    byKey.set(String(e.key), row)
    if (e.managedId) managedIds.add(String(e.managedId))
  }
  return next
}
 
// 合并同 b_key 的重复透视行,保证一个键仅一行。
// 返回新的数组,不修改入参。
export function consolidatePivotRows(rows) {
  const byKey = new Map()
  const merged = []
  for (const row of rows) {
    const key = String(row.b_key || '')
    const hit = byKey.get(key)
    if (hit) {
      for (const [k, v] of Object.entries(row)) {
        if (k === '_id' || k === '_localeIds') continue
        // 仅当目标为空(null/undefined/空串)且来源非空时才补全,保留首个非空值
        if ((hit[k] == null || hit[k] === '') && v != null) hit[k] = v
      }
      Object.assign(hit._localeIds || (hit._localeIds = {}), row._localeIds || {})
      if (row._managedId && !hit._managedId) hit._managedId = row._managedId
      if (row._syncInjected && !hit._syncInjected) hit._syncInjected = row._syncInjected
      if (row._defaultValue != null && hit._defaultValue == null)
        hit._defaultValue = row._defaultValue
      if (row._sortType && !hit._sortType) hit._sortType = row._sortType
    } else {
      byKey.set(key, row)
      merged.push(row)
    }
  }
  return merged
}