Files
mdm-ordinis/ordinis-admin-ui/src/lib/locales.ts
T
Zimin A.N. 5e1b39db03 fix(dict): name column в records list — pick localized value, не [object Object]
Раньше для записей с x-localized name (КА, антенна, ground_station,
operator) колонка 'name / code' показывала literal '[object Object]'
потому что String({'ru-RU': 'Канопус', 'en-US': 'Kanopus'}) =
toString() стандартный → '[object Object]'.

Решение:
- Новый shared util lib/locales.ts:
  - pickLocalized(value, defaultLocale) — извлекает строку из i18n
    объекта или plain string. Fallback на first non-empty key
    если defaultLocale не найден.
  - recordDisplayName(data, defaultLocale) — name → code → '—' chain.
- Records list использует recordDisplayName с dict.defaultLocale
  (берётся из DictionaryDetail).
- SchemaDrivenForm дедуплицирован — был свой локальный pickLocalized,
  теперь импортирует из util'ы.

Tests: +13 (76 → 89), unit tests на pickLocalized + recordDisplayName
покрывают все edge cases (plain string, localized object, fallback
chain, null/undefined input).
2026-05-06 21:05:50 +03:00

50 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Извлекает строковое значение из i18n-объекта `{ "ru-RU": "...", "en-US": "..." }`.
* Schema marker — `x-localized: true`. Backend хранит как JSONB-объект, UI
* рендерит на основе текущего locale пользователя.
*
* Логика выбора:
* 1. value уже string → return as-is (legacy / non-localized fallback).
* 2. value object → key === defaultLocale если есть и не пустой.
* 3. иначе первая non-empty value по любому ключу (стабильный порядок
* через Object.keys итерацию).
* 4. nothing → undefined.
*
* Возвращает undefined вместо пустой строки чтобы caller мог показать `'—'` /
* placeholder, а не пустое поле.
*/
export const pickLocalized = (
value: unknown,
defaultLocale: string,
): string | undefined => {
if (typeof value === 'string') return value.length > 0 ? value : undefined
if (!value || typeof value !== 'object') return undefined
const map = value as Record<string, unknown>
const direct = map[defaultLocale]
if (typeof direct === 'string' && direct.length > 0) return direct
for (const key of Object.keys(map)) {
const v = map[key]
if (typeof v === 'string' && v.length > 0) return v
}
return undefined
}
/**
* Display value для record name column. Tries:
* 1. localized name (i18n-object)
* 2. plain `name` string (non-localized dicts)
* 3. `code` field
* 4. fallback '—'
*/
export const recordDisplayName = (
data: Record<string, unknown> | undefined,
defaultLocale: string,
): string => {
if (!data) return '—'
const localized = pickLocalized(data.name, defaultLocale)
if (localized) return localized
const code = data.code
if (typeof code === 'string' && code.length > 0) return code
return '—'
}