@pixi-ui-editor/schema 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +34 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -3
- package/dist/index.js.map +1 -1
- package/dist/localization.d.ts +8 -1
- package/dist/localization.d.ts.map +1 -1
- package/dist/localization.js +13 -0
- package/dist/localization.js.map +1 -1
- package/package.json +1 -1
package/dist/localization.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Static } from "@sinclair/typebox";
|
|
2
|
-
import type { ProjectDocument, ValidationIssue } from "./index.js";
|
|
2
|
+
import type { ProjectDocument, TextCase, ValidationIssue } from "./index.js";
|
|
3
3
|
export declare const LocalizationLocaleSchema: import("@sinclair/typebox").TObject<{
|
|
4
4
|
id: import("@sinclair/typebox").TString;
|
|
5
5
|
label: import("@sinclair/typebox").TString;
|
|
@@ -91,6 +91,13 @@ export type LocalizedTextResolution = {
|
|
|
91
91
|
*/
|
|
92
92
|
export declare function describeLocalizedText(input: ResolveLocalizedTextInput): LocalizedTextResolution;
|
|
93
93
|
export declare function resolveLocalizedText(input: ResolveLocalizedTextInput): string;
|
|
94
|
+
/**
|
|
95
|
+
* `Text.textCase` display transform: applied last, on top of whatever `describeLocalizedText`
|
|
96
|
+
* resolved (authored string, live preview, or translation) — never written back into `text` or a
|
|
97
|
+
* catalog entry, so the source string and the case choice stay independently editable. Omitted/`"none"`
|
|
98
|
+
* is a no-op, keeping every existing document's rendered output unchanged.
|
|
99
|
+
*/
|
|
100
|
+
export declare function applyTextCase(text: string, textCase: TextCase | undefined): string;
|
|
94
101
|
/** Владелец иерархии, в которой найдена ссылка: сцена или определение пресета. */
|
|
95
102
|
export type LocalizationReferenceOwner = {
|
|
96
103
|
kind: "scene";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../src/localization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAU,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../src/localization.ts"],"names":[],"mappings":"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"}
|
package/dist/localization.js
CHANGED
|
@@ -161,6 +161,19 @@ export function describeLocalizedText(input) {
|
|
|
161
161
|
export function resolveLocalizedText(input) {
|
|
162
162
|
return describeLocalizedText(input).value;
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* `Text.textCase` display transform: applied last, on top of whatever `describeLocalizedText`
|
|
166
|
+
* resolved (authored string, live preview, or translation) — never written back into `text` or a
|
|
167
|
+
* catalog entry, so the source string and the case choice stay independently editable. Omitted/`"none"`
|
|
168
|
+
* is a no-op, keeping every existing document's rendered output unchanged.
|
|
169
|
+
*/
|
|
170
|
+
export function applyTextCase(text, textCase) {
|
|
171
|
+
switch (textCase) {
|
|
172
|
+
case "uppercase": return text.toUpperCase();
|
|
173
|
+
case "lowercase": return text.toLowerCase();
|
|
174
|
+
default: return text;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
164
177
|
/**
|
|
165
178
|
* Все ссылки `Text.textKey` документа: сцены, определения пресетов, локально добавленные узлы любой
|
|
166
179
|
* вложенности и persisted property overrides. Унаследованные узлы инстанса ключ не «размножают»:
|
package/dist/localization.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization.js","sourceRoot":"","sources":["../src/localization.ts"],"names":[],"mappings":"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","sourcesContent":["import { Type, type Static } from \"@sinclair/typebox\";\r\nimport type { ProjectDocument, UINode, ValidationIssue } from \"./index.js\";\r\nimport { materializedPrefabNodeId } from \"./prefabs.js\";\r\n\r\n/**\r\n * Единственная реализация локализации документа: контракт каталога, канонизация locale ID,\r\n * fallback-цепочка, интерполяция параметров, сбор ссылок `Text.textKey`, покрытие и диагностики.\r\n *\r\n * Runtime, экспорт, редактор и демо обязаны переиспользовать именно её: дублирование fallback- или\r\n * interpolation-логики в другом слое немедленно расходится по поведению.\r\n */\r\n\r\nconst LocaleIdSchema = Type.String({ minLength: 1 });\r\nexport const LocalizationLocaleSchema = Type.Object({ id: LocaleIdSchema, label: Type.String({ minLength: 1 }) });\r\nexport type LocalizationLocale = Static<typeof LocalizationLocaleSchema>;\r\n/**\r\n * `entries[key][localeId]` — плоская строка перевода. Отсутствующий перевод — это отсутствие\r\n * свойства локали, а не пустая строка: `\"\"` является валидным заполненным переводом и обрывает\r\n * fallback, поэтому резолвер нигде не проверяет значения через truthiness.\r\n */\r\nexport const LocalizationSchema = Type.Object({\r\n baseLocale: LocaleIdSchema,\r\n locales: Type.Array(LocalizationLocaleSchema),\r\n entries: Type.Record(Type.String({ minLength: 1 }), Type.Record(Type.String({ minLength: 1 }), Type.String())),\r\n});\r\nexport type LocalizationCatalog = Static<typeof LocalizationSchema>;\r\n\r\nexport const DEFAULT_LOCALE_ID = \"en\";\r\n/** Каталог, который получает документ v19 при миграции и любой новый проект. */\r\nexport function createDefaultLocalization(): LocalizationCatalog {\r\n return { baseLocale: DEFAULT_LOCALE_ID, locales: [{ id: DEFAULT_LOCALE_ID, label: \"English\" }], entries: {} };\r\n}\r\n\r\n/**\r\n * Каноническая BCP-47 форма: `en`, `en-US`, `pt-BR`, `zh-Hant`, `zh-Hant-TW`.\r\n *\r\n * Грамматика намеренно узкая — только буквы, три цифры региона и дефис. Поэтому канонический ID\r\n * автоматически является безопасным сегментом пути `locales/<locale>.json`: в нём не может быть\r\n * ни `/`, ни `\\`, ни `.`, ни `..`.\r\n */\r\nexport function canonicalizeLocaleId(input: string): string | undefined {\r\n const parts = input.trim().split(\"-\");\r\n const language = parts[0];\r\n if (language === undefined || !/^[A-Za-z]{2,3}$/.test(language)) return undefined;\r\n const segments = [language.toLowerCase()];\r\n let index = 1;\r\n const script = parts[index];\r\n if (script !== undefined && /^[A-Za-z]{4}$/.test(script)) { segments.push(`${script[0]!.toUpperCase()}${script.slice(1).toLowerCase()}`); index += 1; }\r\n const region = parts[index];\r\n if (region !== undefined) {\r\n if (/^[A-Za-z]{2}$/.test(region)) segments.push(region.toUpperCase());\r\n else if (/^[0-9]{3}$/.test(region)) segments.push(region);\r\n else return undefined;\r\n index += 1;\r\n }\r\n return index === parts.length ? segments.join(\"-\") : undefined;\r\n}\r\n\r\nexport function isCanonicalLocaleId(id: string): boolean { return canonicalizeLocaleId(id) === id; }\r\n\r\n/**\r\n * Справочник популярных локалей для UI выбора языка: готовый ID в канонической форме плюс\r\n * английский display label. Он живёт рядом с `canonicalizeLocaleId`, потому что это часть\r\n * локализационной модели, а не украшение панели: каждая запись обязана удовлетворять\r\n * `canonicalizeLocaleId(id) === id`, иначе выбор из списка создал бы невалидный документ.\r\n *\r\n * Список намеренно неполон — он покрывает частые случаи и снимает ручной ввод, а редкая локаль\r\n * по-прежнему добавляется вводом канонического BCP-47 идентификатора вручную.\r\n */\r\nexport const COMMON_LOCALES: readonly { id: string; label: string }[] = [\r\n { id: \"en\", label: \"English\" },\r\n { id: \"en-US\", label: \"English (United States)\" },\r\n { id: \"en-GB\", label: \"English (United Kingdom)\" },\r\n { id: \"ru\", label: \"Russian\" },\r\n { id: \"ru-RU\", label: \"Russian (Russia)\" },\r\n { id: \"uk\", label: \"Ukrainian\" },\r\n { id: \"de\", label: \"German\" },\r\n { id: \"fr\", label: \"French\" },\r\n { id: \"fr-CA\", label: \"French (Canada)\" },\r\n { id: \"es\", label: \"Spanish\" },\r\n { id: \"es-ES\", label: \"Spanish (Spain)\" },\r\n { id: \"es-419\", label: \"Spanish (Latin America)\" },\r\n { id: \"pt-BR\", label: \"Portuguese (Brazil)\" },\r\n { id: \"pt-PT\", label: \"Portuguese (Portugal)\" },\r\n { id: \"it\", label: \"Italian\" },\r\n { id: \"pl\", label: \"Polish\" },\r\n { id: \"tr\", label: \"Turkish\" },\r\n { id: \"nl\", label: \"Dutch\" },\r\n { id: \"sv\", label: \"Swedish\" },\r\n { id: \"da\", label: \"Danish\" },\r\n { id: \"nb\", label: \"Norwegian Bokmal\" },\r\n { id: \"fi\", label: \"Finnish\" },\r\n { id: \"cs\", label: \"Czech\" },\r\n { id: \"sk\", label: \"Slovak\" },\r\n { id: \"hu\", label: \"Hungarian\" },\r\n { id: \"ro\", label: \"Romanian\" },\r\n { id: \"bg\", label: \"Bulgarian\" },\r\n { id: \"el\", label: \"Greek\" },\r\n { id: \"sr\", label: \"Serbian\" },\r\n { id: \"hr\", label: \"Croatian\" },\r\n { id: \"ar\", label: \"Arabic\" },\r\n { id: \"he\", label: \"Hebrew\" },\r\n { id: \"fa\", label: \"Persian\" },\r\n { id: \"hi\", label: \"Hindi\" },\r\n { id: \"th\", label: \"Thai\" },\r\n { id: \"vi\", label: \"Vietnamese\" },\r\n { id: \"id\", label: \"Indonesian\" },\r\n { id: \"ms\", label: \"Malay\" },\r\n { id: \"tl\", label: \"Filipino\" },\r\n { id: \"ja\", label: \"Japanese\" },\r\n { id: \"ko\", label: \"Korean\" },\r\n { id: \"zh-Hans\", label: \"Chinese (Simplified)\" },\r\n { id: \"zh-Hant\", label: \"Chinese (Traditional)\" },\r\n];\r\n\r\n/**\r\n * Порядок локалей для любого UI: base locale первой, остальные сохраняют порядок объявления.\r\n * Правило одно на весь редактор, поэтому и живёт оно здесь, а не копией в каждой панели.\r\n */\r\nexport function orderLocalesForDisplay(catalog: LocalizationCatalog): LocalizationLocale[] {\r\n return [\r\n ...catalog.locales.filter((locale) => locale.id === catalog.baseLocale),\r\n ...catalog.locales.filter((locale) => locale.id !== catalog.baseLocale),\r\n ];\r\n}\r\n\r\nexport const LOCALIZATION_DIRECTORY = \"locales\";\r\nexport const LOCALIZATION_COVERAGE_FILE = `${LOCALIZATION_DIRECTORY}/coverage.json`;\r\n/** Путь словаря внутри пакета. Безопасность сегмента гарантирует канонический ID. */\r\nexport function localeFilePath(localeId: string): string { return `${LOCALIZATION_DIRECTORY}/${localeId}.json`; }\r\n\r\nexport type LocalizationParams = Readonly<Record<string, string | number>>;\r\nconst PARAM_PATTERN = /\\{([A-Za-z0-9_.-]+)\\}/g;\r\n\r\n/** Имена runtime-параметров `{name}` внутри значения перевода, детерминированно отсортированные. */\r\nexport function collectLocalizationParams(value: string): string[] {\r\n return [...new Set([...value.matchAll(PARAM_PATTERN)].map((match) => match[1]!))].sort();\r\n}\r\n\r\n/** Неизвестный параметр остаётся видимым в фигурных скобках — это признак незаполненного значения. */\r\nexport function interpolateLocalizedText(value: string, params?: LocalizationParams): string {\r\n if (params === undefined) return value;\r\n return value.replace(PARAM_PATTERN, (match, name: string) => (Object.hasOwn(params, name) ? String(params[name]) : match));\r\n}\r\n\r\nexport type LocalizationTranslateContext = { key: string; locale: string; baseLocale: string; params?: LocalizationParams };\r\n/** Внешний переводчик потребителя. `undefined` означает «нет мнения» и не обрывает fallback. */\r\nexport type TranslateFn = (key: string, context: LocalizationTranslateContext) => string | undefined;\r\n\r\nexport type ResolveLocalizedTextInput = {\r\n /** Авторская строка `Text.text`: ручной режим и последний содержательный fallback. */\r\n text: string;\r\n textKey?: string;\r\n catalog?: LocalizationCatalog;\r\n locale?: string;\r\n translate?: TranslateFn;\r\n params?: LocalizationParams;\r\n};\r\n\r\n/** Откуда взялась итоговая строка. `literal` — у ноды нет ключа и локализация не участвовала вообще. */\r\nexport type LocalizedTextSource = \"literal\" | \"translate\" | \"locale\" | \"baseLocale\" | \"text\" | \"key\";\r\nexport type LocalizedTextResolution = { value: string; source: LocalizedTextSource };\r\n\r\n/**\r\n * Единственная fallback-цепочка: внешний `translate` → выбранная локаль → базовая локаль →\r\n * `Text.text` → сам ключ. Нода без `textKey` не обращается к локализации вообще, поэтому документ\r\n * без ключей выглядит ровно как до появления каталога.\r\n *\r\n * Возвращает и значение, и источник: редактору нужно показать, почему на макете именно эта строка,\r\n * и он обязан читать это из той же реализации, а не выводить цепочку заново.\r\n */\r\nexport function describeLocalizedText(input: ResolveLocalizedTextInput): LocalizedTextResolution {\r\n const key = input.textKey?.trim();\r\n if (key === undefined || key === \"\") return { value: input.text, source: \"literal\" };\r\n const baseLocale = input.catalog?.baseLocale ?? DEFAULT_LOCALE_ID;\r\n const locale = input.locale ?? baseLocale;\r\n const entry = input.catalog?.entries[key];\r\n const external = input.translate?.(key, { key, locale, baseLocale, params: input.params });\r\n const resolution: LocalizedTextResolution = external !== undefined\r\n ? { value: external, source: \"translate\" }\r\n : entry !== undefined && Object.hasOwn(entry, locale) ? { value: entry[locale]!, source: \"locale\" }\r\n : entry !== undefined && Object.hasOwn(entry, baseLocale) ? { value: entry[baseLocale]!, source: \"baseLocale\" }\r\n : input.text.length > 0 ? { value: input.text, source: \"text\" } : { value: key, source: \"key\" };\r\n return { value: interpolateLocalizedText(resolution.value, input.params), source: resolution.source };\r\n}\r\n\r\nexport function resolveLocalizedText(input: ResolveLocalizedTextInput): string {\r\n return describeLocalizedText(input).value;\r\n}\r\n\r\n/** Владелец иерархии, в которой найдена ссылка: сцена или определение пресета. */\r\nexport type LocalizationReferenceOwner = { kind: \"scene\"; id: string } | { kind: \"prefab\"; id: string };\r\nexport type LocalizationKeyReference = {\r\n key: string;\r\n path: string;\r\n owner: LocalizationReferenceOwner;\r\n /**\r\n * Адрес узла в терминах редактора: собственный id персистентного/локального узла либо\r\n * materialized-адрес `instanceId::prefab-node::nodePath` для property override.\r\n */\r\n nodeId: string;\r\n};\r\n/** Ссылка вместе с записью значения: одна и та же реализация обхода читает и переименовывает ключи. */\r\nexport type LocalizationKeyReferenceSite = LocalizationKeyReference & { setKey(next: string): void };\r\n\r\n/**\r\n * Все ссылки `Text.textKey` документа: сцены, определения пресетов, локально добавленные узлы любой\r\n * вложенности и persisted property overrides. Унаследованные узлы инстанса ключ не «размножают»:\r\n * их источником является узел определения, уже посчитанный обходом `document.prefabs`.\r\n *\r\n * Обход единственный: `collectTextKeyReferences` только собирает результат, а rename каталога\r\n * переписывает ключи через `setKey`, поэтому «где живут ссылки» описано ровно в одном месте.\r\n */\r\nexport function forEachTextKeyReference(document: ProjectDocument, visit: (site: LocalizationKeyReferenceSite) => void): void {\r\n const visitNode = (node: UINode, path: string, owner: LocalizationReferenceOwner): void => {\r\n if (node.type === \"text\" && node.textKey !== undefined) {\r\n visit({ key: node.textKey, path: `${path}/textKey`, owner, nodeId: node.id, setKey: (next) => { node.textKey = next; } });\r\n }\r\n if (node.type !== \"prefab-instance\") return;\r\n (node.propertyOverrides ?? []).forEach((override, index) => {\r\n const value = override.patch.textKey;\r\n if (typeof value !== \"string\") return;\r\n visit({\r\n key: value,\r\n path: `${path}/propertyOverrides/${index}/patch/textKey`,\r\n owner,\r\n nodeId: materializedPrefabNodeId(node.id, override.nodeId),\r\n setKey: (next) => { override.patch.textKey = next; },\r\n });\r\n });\r\n (node.locallyAddedNodes ?? []).forEach((local, index) => visitNode(local, `${path}/locallyAddedNodes/${index}`, owner));\r\n };\r\n document.scenes.forEach((scene, sceneIndex) => scene.nodes.forEach((node, nodeIndex) => visitNode(node, `/scenes/${sceneIndex}/nodes/${nodeIndex}`, { kind: \"scene\", id: scene.id })));\r\n document.prefabs.forEach((prefab, prefabIndex) => prefab.nodes.forEach((node, nodeIndex) => visitNode(node, `/prefabs/${prefabIndex}/nodes/${nodeIndex}`, { kind: \"prefab\", id: prefab.id })));\r\n}\r\n\r\nexport function collectTextKeyReferences(document: ProjectDocument): LocalizationKeyReference[] {\r\n const references: LocalizationKeyReference[] = [];\r\n forEachTextKeyReference(document, ({ key, path, owner, nodeId }) => references.push({ key, path, owner, nodeId }));\r\n return references;\r\n}\r\n\r\nexport type LocalizationLocaleCoverage = { id: string; label: string; isBase: boolean; translated: number; missingKeys: string[] };\r\nexport type LocalizationCoverage = {\r\n baseLocale: string;\r\n /** Все ключи каталога — покрытие считается по каталогу, а не по текущему usage. */\r\n keys: string[];\r\n keyCount: number;\r\n locales: LocalizationLocaleCoverage[];\r\n usedKeys: string[];\r\n unusedKeys: string[];\r\n /** Ключи, на которые ссылается `Text`, но которых нет в каталоге. */\r\n missingKeys: string[];\r\n};\r\n\r\n/** Детерминированное покрытие: и ключи, и локали, и списки отсортированы. */\r\nexport function computeLocalizationCoverage(document: ProjectDocument): LocalizationCoverage {\r\n const catalog = document.localization;\r\n const keys = Object.keys(catalog.entries).sort();\r\n const referenced = new Set(collectTextKeyReferences(document).map((reference) => reference.key.trim()).filter((key) => key !== \"\"));\r\n const usedKeys = keys.filter((key) => referenced.has(key));\r\n return {\r\n baseLocale: catalog.baseLocale,\r\n keys,\r\n keyCount: keys.length,\r\n locales: catalog.locales.map((locale) => {\r\n const missingKeys = keys.filter((key) => !Object.hasOwn(catalog.entries[key]!, locale.id));\r\n return { id: locale.id, label: locale.label, isBase: locale.id === catalog.baseLocale, translated: keys.length - missingKeys.length, missingKeys };\r\n }),\r\n usedKeys,\r\n unusedKeys: keys.filter((key) => !referenced.has(key)),\r\n missingKeys: [...referenced].filter((key) => !Object.hasOwn(catalog.entries, key)).sort(),\r\n };\r\n}\r\n\r\nconst issue = (code: string, path: string, message: string, severity: ValidationIssue[\"severity\"]): ValidationIssue => ({ code, path, message, severity });\r\n\r\n/**\r\n * Structural/semantic ошибки каталога: они делают документ невалидным.\r\n *\r\n * Здесь только то, что нельзя доставить ни при каком заполнении переводов: неканоничный или\r\n * дублирующийся locale ID, неизвестная base locale и пустая ссылка на ключ.\r\n */\r\nexport function collectLocalizationErrors(document: ProjectDocument): ValidationIssue[] {\r\n const issues: ValidationIssue[] = [];\r\n const catalog = document.localization;\r\n const seen = new Set<string>();\r\n catalog.locales.forEach((locale, index) => {\r\n const path = `/localization/locales/${index}/id`;\r\n if (!isCanonicalLocaleId(locale.id)) issues.push(issue(\"INVALID_LOCALE_ID\", path, `Locale '${locale.id}' is not a canonical BCP-47 identifier.`, \"error\"));\r\n if (seen.has(locale.id)) issues.push(issue(\"DUPLICATE_LOCALE\", path, `Locale '${locale.id}' is declared more than once.`, \"error\"));\r\n seen.add(locale.id);\r\n });\r\n if (!seen.has(catalog.baseLocale)) issues.push(issue(\"UNKNOWN_BASE_LOCALE\", \"/localization/baseLocale\", `Base locale '${catalog.baseLocale}' is not declared.`, \"error\"));\r\n for (const key of Object.keys(catalog.entries)) {\r\n if (key.trim() === \"\") issues.push(issue(\"EMPTY_LOCALIZATION_KEY\", `/localization/entries/${key}`, \"A localization key must not be blank.\", \"error\"));\r\n }\r\n for (const reference of collectTextKeyReferences(document)) {\r\n if (reference.key.trim() === \"\") issues.push(issue(\"EMPTY_LOCALIZATION_KEY\", reference.path, \"A textKey reference must not be blank.\", \"error\"));\r\n }\r\n return issues;\r\n}\r\n\r\n/**\r\n * Authoring diagnostics: каталог заполняется постепенно, поэтому всё это — warnings, и документ с\r\n * ними остаётся валидным и сохраняемым.\r\n */\r\nexport function collectLocalizationWarnings(document: ProjectDocument): ValidationIssue[] {\r\n const issues: ValidationIssue[] = [];\r\n const catalog = document.localization;\r\n const coverage = computeLocalizationCoverage(document);\r\n const declared = new Set(catalog.locales.map((locale) => locale.id));\r\n for (const reference of collectTextKeyReferences(document)) {\r\n const key = reference.key.trim();\r\n if (key !== \"\" && !Object.hasOwn(catalog.entries, key)) issues.push(issue(\"MISSING_LOCALIZATION_ENTRY\", reference.path, `Localization key '${key}' is not in the catalog.`, \"warning\"));\r\n }\r\n for (const key of coverage.keys) {\r\n const entry = catalog.entries[key]!;\r\n if (!Object.hasOwn(entry, catalog.baseLocale)) issues.push(issue(\"MISSING_BASE_TRANSLATION\", `/localization/entries/${key}`, `Key '${key}' has no '${catalog.baseLocale}' translation.`, \"warning\"));\r\n const baseParams = Object.hasOwn(entry, catalog.baseLocale) ? collectLocalizationParams(entry[catalog.baseLocale]!).join(\",\") : undefined;\r\n for (const localeId of Object.keys(entry).sort()) {\r\n if (!declared.has(localeId)) { issues.push(issue(\"UNKNOWN_TRANSLATION_LOCALE\", `/localization/entries/${key}/${localeId}`, `Locale '${localeId}' is not declared and will not be exported.`, \"warning\")); continue; }\r\n if (baseParams === undefined || localeId === catalog.baseLocale) continue;\r\n if (collectLocalizationParams(entry[localeId]!).join(\",\") !== baseParams) issues.push(issue(\"PARAM_MISMATCH\", `/localization/entries/${key}/${localeId}`, `Translation '${key}' for '${localeId}' uses different runtime parameters than the base locale.`, \"warning\"));\r\n }\r\n }\r\n for (const locale of coverage.locales) {\r\n if (locale.isBase || locale.missingKeys.length === 0) continue;\r\n issues.push(issue(\"INCOMPLETE_LOCALE\", `/localization/locales/${catalog.locales.findIndex((candidate) => candidate.id === locale.id)}`, `Locale '${locale.id}' is missing ${locale.missingKeys.length} translation(s).`, \"warning\"));\r\n }\r\n for (const key of coverage.unusedKeys) {\r\n issues.push(issue(\"UNUSED_LOCALIZATION_KEY\", `/localization/entries/${key}`, `Key '${key}' is not referenced by any delivered Text node.`, \"warning\"));\r\n }\r\n return issues;\r\n}\r\n\r\nexport function collectLocalizationIssues(document: ProjectDocument): ValidationIssue[] {\r\n return [...collectLocalizationErrors(document), ...collectLocalizationWarnings(document)];\r\n}\r\n\r\n/**\r\n * Строгая export-проверка: то же состояние, что в авторинге является warning, для пакета является\r\n * блокирующей ошибкой, если ключ каталога вообще нельзя доставить — у него нет base translation.\r\n * Неполные secondary locales и unused keys экспорт не блокируют и живут в coverage-отчёте.\r\n */\r\nexport function collectLocalizationExportErrors(document: ProjectDocument): ValidationIssue[] {\r\n const catalog = document.localization;\r\n const issues = collectLocalizationErrors(document);\r\n for (const key of Object.keys(catalog.entries).sort()) {\r\n if (!Object.hasOwn(catalog.entries[key]!, catalog.baseLocale)) issues.push(issue(\"MISSING_BASE_TRANSLATION\", `/localization/entries/${key}`, `Key '${key}' cannot be delivered: it has no '${catalog.baseLocale}' translation.`, \"error\"));\r\n }\r\n return issues;\r\n}\r\n\r\n/**\r\n * Словарь одной локали: плоский `Record<string, string>` без metadata wrapper, готовый любому\r\n * внешнему i18n. Ключ попадает в файл только если у него есть значение именно этой локали;\r\n * отсутствующий перевод не превращается в `\"\"`, а остаётся отмеченным в coverage.\r\n */\r\nexport function buildLocaleDictionary(catalog: LocalizationCatalog, localeId: string): Record<string, string> {\r\n const dictionary: Record<string, string> = {};\r\n for (const key of Object.keys(catalog.entries).sort()) {\r\n const entry = catalog.entries[key]!;\r\n if (Object.hasOwn(entry, localeId)) dictionary[key] = entry[localeId]!;\r\n }\r\n return dictionary;\r\n}\r\n\r\nexport function buildLocaleDictionaries(catalog: LocalizationCatalog): Record<string, Record<string, string>> {\r\n return Object.fromEntries(catalog.locales.map((locale) => [locale.id, buildLocaleDictionary(catalog, locale.id)]));\r\n}\r\n\r\nexport type LocalizationCoverageReport = LocalizationCoverage & { warnings: ValidationIssue[] };\r\n/** Отчёт покрытия пакета: он дополняет словари, а не заменяет их. */\r\nexport function buildLocalizationCoverageReport(document: ProjectDocument): LocalizationCoverageReport {\r\n return { ...computeLocalizationCoverage(document), warnings: collectLocalizationWarnings(document) };\r\n}\r\n\r\n/**\r\n * Localization-часть пакета: по одному плоскому словарю на каждую объявленную локаль плюс отчёт\r\n * покрытия. Каталог экспортируется целиком: unused-ключ может быть контрактом игрового кода, а\r\n * usage текущей иерархии не доказывает, что он не нужен.\r\n *\r\n * Раскладка описана здесь и только здесь: и ZIP-экспорт редактора, и фикстура демо обязаны звать\r\n * эту функцию, иначе демо проверяло бы не тот контракт. Документ ожидается уже подготовленным к\r\n * доставке (без `editorOnly` поддеревьев), потому что coverage говорит о доставляемых `Text`.\r\n */\r\nexport function collectPackageLocaleEntries(document: ProjectDocument): Record<string, string> {\r\n const entries: Record<string, string> = {};\r\n for (const locale of document.localization.locales) {\r\n entries[localeFilePath(locale.id)] = `${JSON.stringify(buildLocaleDictionary(document.localization, locale.id), null, 2)}\\n`;\r\n }\r\n entries[LOCALIZATION_COVERAGE_FILE] = `${JSON.stringify(buildLocalizationCoverageReport(document), null, 2)}\\n`;\r\n return entries;\r\n}\r\n\r\n/**\r\n * Блокировка экспорта каталогом. Сообщение формируется ровно здесь, чтобы UI показывал ту же\r\n * диагностику, что попадает в консоль, а не собственную формулировку.\r\n */\r\nexport class LocalizationExportError extends Error {\r\n readonly issues: ValidationIssue[];\r\n\r\n constructor(issues: ValidationIssue[]) {\r\n super(`The localization catalog cannot be packaged:\\n${issues.map((item) => `• ${item.message} (${item.code} at ${item.path})`).join(\"\\n\")}`);\r\n this.name = \"LocalizationExportError\";\r\n this.issues = issues;\r\n }\r\n}\r\n\r\n/**\r\n * Строгий pre-export gate. То, что в авторинге остаётся warning, здесь блокирует пакет, если ключ\r\n * невозможно доставить вообще: у него нет перевода базовой локали. Неполные secondary locales и\r\n * unused keys экспорт не останавливают и попадают только в `locales/coverage.json`.\r\n */\r\nexport function assertExportableLocalization(document: ProjectDocument): void {\r\n const errors = collectLocalizationExportErrors(document);\r\n if (errors.length > 0) throw new LocalizationExportError(errors);\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"localization.js","sourceRoot":"","sources":["../src/localization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAe,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAExD;;;;;;GAMG;AAEH,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;AACrD,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAElH;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,UAAU,EAAE,cAAc;IAC1B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;CAC/G,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AACtC,gFAAgF;AAChF,MAAM,UAAU,yBAAyB;IACvC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAChH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAClF,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,MAAM,KAAK,SAAS,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAAC,KAAK,IAAI,CAAC,CAAC;IAAC,CAAC;IACvJ,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;aACjE,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;;YACrD,OAAO,SAAS,CAAC;QACtB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IACD,OAAO,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,EAAU,IAAa,OAAO,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AAEpG;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAA6C;IACtE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,yBAAyB,EAAE;IACjD,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,0BAA0B,EAAE;IAClD,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAC1C,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE;IAChC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACzC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACzC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,EAAE;IAClD,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE;IAC7C,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE;IAC/C,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;IAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,kBAAkB,EAAE;IACvC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;IAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE;IAChC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;IAC/B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE;IAChC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;IAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;IAC/B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;IAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;IAC3B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE;IACjC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE;IACjC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;IAC5B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;IAC/B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;IAC/B,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC7B,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAChD,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE;CAClD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA4B;IACjE,OAAO;QACL,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC;QACvE,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC;KACxE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAChD,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,sBAAsB,gBAAgB,CAAC;AACpF,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,QAAgB,IAAY,OAAO,GAAG,sBAAsB,IAAI,QAAQ,OAAO,CAAC,CAAC,CAAC;AAGjH,MAAM,aAAa,GAAG,wBAAwB,CAAC;AAE/C,oGAAoG;AACpG,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3F,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,wBAAwB,CAAC,KAAa,EAAE,MAA2B;IACjF,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7H,CAAC;AAoBD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAgC;IACpE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;IAClC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACrF,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,UAAU,IAAI,iBAAiB,CAAC;IAClE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,UAAU,CAAC;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3F,MAAM,UAAU,GAA4B,QAAQ,KAAK,SAAS;QAChE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE;QAC1C,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;YACjG,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,CAAE,EAAE,MAAM,EAAE,YAAY,EAAE;gBAC7G,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACtG,OAAO,EAAE,KAAK,EAAE,wBAAwB,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;AACxG,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAgC;IACnE,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAA8B;IACxE,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5C,KAAK,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5C,SAAS,OAAO,IAAI,CAAC;IACvB,CAAC;AACH,CAAC;AAiBD;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAyB,EAAE,KAAmD;IACpH,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,IAAY,EAAE,KAAiC,EAAQ,EAAE;QACxF,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACvD,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5H,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB;YAAE,OAAO;QAC5C,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;YACzD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;YACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,OAAO;YACtC,KAAK,CAAC;gBACJ,GAAG,EAAE,KAAK;gBACV,IAAI,EAAE,GAAG,IAAI,sBAAsB,KAAK,gBAAgB;gBACxD,KAAK;gBACL,MAAM,EAAE,wBAAwB,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC;gBAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;aACrD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,IAAI,sBAAsB,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1H,CAAC,CAAC;IACF,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,UAAU,UAAU,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACvL,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,YAAY,WAAW,UAAU,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACjM,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,QAAyB;IAChE,MAAM,UAAU,GAA+B,EAAE,CAAC;IAClD,uBAAuB,CAAC,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACnH,OAAO,UAAU,CAAC;AACpB,CAAC;AAeD,6EAA6E;AAC7E,MAAM,UAAU,2BAA2B,CAAC,QAAyB;IACnE,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;IACpI,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,IAAI;QACJ,QAAQ,EAAE,IAAI,CAAC,MAAM;QACrB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACtC,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3F,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;QACrJ,CAAC,CAAC;QACF,QAAQ;QACR,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtD,WAAW,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;KAC1F,CAAC;AACJ,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,IAAY,EAAE,OAAe,EAAE,QAAqC,EAAmB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;AAE3J;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAyB;IACjE,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,yBAAyB,KAAK,KAAK,CAAC;QACjD,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,IAAI,EAAE,WAAW,MAAM,CAAC,EAAE,yCAAyC,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3J,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,IAAI,EAAE,WAAW,MAAM,CAAC,EAAE,+BAA+B,EAAE,OAAO,CAAC,CAAC,CAAC;QACpI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,0BAA0B,EAAE,gBAAgB,OAAO,CAAC,UAAU,oBAAoB,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1K,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,wBAAwB,EAAE,yBAAyB,GAAG,EAAE,EAAE,uCAAuC,EAAE,OAAO,CAAC,CAAC,CAAC;IACxJ,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,wBAAwB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,wBAAwB,EAAE,SAAS,CAAC,IAAI,EAAE,wCAAwC,EAAE,OAAO,CAAC,CAAC,CAAC;IACnJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAyB;IACnE,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC;IACtC,MAAM,QAAQ,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,KAAK,MAAM,SAAS,IAAI,wBAAwB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,4BAA4B,EAAE,SAAS,CAAC,IAAI,EAAE,qBAAqB,GAAG,0BAA0B,EAAE,SAAS,CAAC,CAAC,CAAC;IAC1L,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,0BAA0B,EAAE,yBAAyB,GAAG,EAAE,EAAE,QAAQ,GAAG,aAAa,OAAO,CAAC,UAAU,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC;QACrM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1I,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,4BAA4B,EAAE,yBAAyB,GAAG,IAAI,QAAQ,EAAE,EAAE,WAAW,QAAQ,6CAA6C,EAAE,SAAS,CAAC,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YACrN,IAAI,UAAU,KAAK,SAAS,IAAI,QAAQ,KAAK,OAAO,CAAC,UAAU;gBAAE,SAAS;YAC1E,IAAI,yBAAyB,CAAC,KAAK,CAAC,QAAQ,CAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,UAAU;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,yBAAyB,GAAG,IAAI,QAAQ,EAAE,EAAE,gBAAgB,GAAG,UAAU,QAAQ,2DAA2D,EAAE,SAAS,CAAC,CAAC,CAAC;QAC1Q,CAAC;IACH,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC/D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,yBAAyB,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,MAAM,CAAC,EAAE,gBAAgB,MAAM,CAAC,WAAW,CAAC,MAAM,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC;IACvO,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,yBAAyB,EAAE,yBAAyB,GAAG,EAAE,EAAE,QAAQ,GAAG,iDAAiD,EAAE,SAAS,CAAC,CAAC,CAAC;IACzJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,QAAyB;IACjE,OAAO,CAAC,GAAG,yBAAyB,CAAC,QAAQ,CAAC,EAAE,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAAC,QAAyB;IACvE,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC;IACtC,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAE,EAAE,OAAO,CAAC,UAAU,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,0BAA0B,EAAE,yBAAyB,GAAG,EAAE,EAAE,QAAQ,GAAG,qCAAqC,OAAO,CAAC,UAAU,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7O,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA4B,EAAE,QAAgB;IAClF,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC;QACpC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAE,CAAC;IACzE,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAA4B;IAClE,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACrH,CAAC;AAGD,qEAAqE;AACrE,MAAM,UAAU,+BAA+B,CAAC,QAAyB;IACvE,OAAO,EAAE,GAAG,2BAA2B,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,2BAA2B,CAAC,QAAQ,CAAC,EAAE,CAAC;AACvG,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAyB;IACnE,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QACnD,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAC/H,CAAC;IACD,OAAO,CAAC,0BAA0B,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,+BAA+B,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAChH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACvC,MAAM,CAAoB;IAEnC,YAAY,MAAyB;QACnC,KAAK,CAAC,iDAAiD,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9I,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,QAAyB;IACpE,MAAM,MAAM,GAAG,+BAA+B,CAAC,QAAQ,CAAC,CAAC;IACzD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import { Type, type Static } from \"@sinclair/typebox\";\r\nimport type { ProjectDocument, TextCase, UINode, ValidationIssue } from \"./index.js\";\r\nimport { materializedPrefabNodeId } from \"./prefabs.js\";\r\n\r\n/**\r\n * Единственная реализация локализации документа: контракт каталога, канонизация locale ID,\r\n * fallback-цепочка, интерполяция параметров, сбор ссылок `Text.textKey`, покрытие и диагностики.\r\n *\r\n * Runtime, экспорт, редактор и демо обязаны переиспользовать именно её: дублирование fallback- или\r\n * interpolation-логики в другом слое немедленно расходится по поведению.\r\n */\r\n\r\nconst LocaleIdSchema = Type.String({ minLength: 1 });\r\nexport const LocalizationLocaleSchema = Type.Object({ id: LocaleIdSchema, label: Type.String({ minLength: 1 }) });\r\nexport type LocalizationLocale = Static<typeof LocalizationLocaleSchema>;\r\n/**\r\n * `entries[key][localeId]` — плоская строка перевода. Отсутствующий перевод — это отсутствие\r\n * свойства локали, а не пустая строка: `\"\"` является валидным заполненным переводом и обрывает\r\n * fallback, поэтому резолвер нигде не проверяет значения через truthiness.\r\n */\r\nexport const LocalizationSchema = Type.Object({\r\n baseLocale: LocaleIdSchema,\r\n locales: Type.Array(LocalizationLocaleSchema),\r\n entries: Type.Record(Type.String({ minLength: 1 }), Type.Record(Type.String({ minLength: 1 }), Type.String())),\r\n});\r\nexport type LocalizationCatalog = Static<typeof LocalizationSchema>;\r\n\r\nexport const DEFAULT_LOCALE_ID = \"en\";\r\n/** Каталог, который получает документ v19 при миграции и любой новый проект. */\r\nexport function createDefaultLocalization(): LocalizationCatalog {\r\n return { baseLocale: DEFAULT_LOCALE_ID, locales: [{ id: DEFAULT_LOCALE_ID, label: \"English\" }], entries: {} };\r\n}\r\n\r\n/**\r\n * Каноническая BCP-47 форма: `en`, `en-US`, `pt-BR`, `zh-Hant`, `zh-Hant-TW`.\r\n *\r\n * Грамматика намеренно узкая — только буквы, три цифры региона и дефис. Поэтому канонический ID\r\n * автоматически является безопасным сегментом пути `locales/<locale>.json`: в нём не может быть\r\n * ни `/`, ни `\\`, ни `.`, ни `..`.\r\n */\r\nexport function canonicalizeLocaleId(input: string): string | undefined {\r\n const parts = input.trim().split(\"-\");\r\n const language = parts[0];\r\n if (language === undefined || !/^[A-Za-z]{2,3}$/.test(language)) return undefined;\r\n const segments = [language.toLowerCase()];\r\n let index = 1;\r\n const script = parts[index];\r\n if (script !== undefined && /^[A-Za-z]{4}$/.test(script)) { segments.push(`${script[0]!.toUpperCase()}${script.slice(1).toLowerCase()}`); index += 1; }\r\n const region = parts[index];\r\n if (region !== undefined) {\r\n if (/^[A-Za-z]{2}$/.test(region)) segments.push(region.toUpperCase());\r\n else if (/^[0-9]{3}$/.test(region)) segments.push(region);\r\n else return undefined;\r\n index += 1;\r\n }\r\n return index === parts.length ? segments.join(\"-\") : undefined;\r\n}\r\n\r\nexport function isCanonicalLocaleId(id: string): boolean { return canonicalizeLocaleId(id) === id; }\r\n\r\n/**\r\n * Справочник популярных локалей для UI выбора языка: готовый ID в канонической форме плюс\r\n * английский display label. Он живёт рядом с `canonicalizeLocaleId`, потому что это часть\r\n * локализационной модели, а не украшение панели: каждая запись обязана удовлетворять\r\n * `canonicalizeLocaleId(id) === id`, иначе выбор из списка создал бы невалидный документ.\r\n *\r\n * Список намеренно неполон — он покрывает частые случаи и снимает ручной ввод, а редкая локаль\r\n * по-прежнему добавляется вводом канонического BCP-47 идентификатора вручную.\r\n */\r\nexport const COMMON_LOCALES: readonly { id: string; label: string }[] = [\r\n { id: \"en\", label: \"English\" },\r\n { id: \"en-US\", label: \"English (United States)\" },\r\n { id: \"en-GB\", label: \"English (United Kingdom)\" },\r\n { id: \"ru\", label: \"Russian\" },\r\n { id: \"ru-RU\", label: \"Russian (Russia)\" },\r\n { id: \"uk\", label: \"Ukrainian\" },\r\n { id: \"de\", label: \"German\" },\r\n { id: \"fr\", label: \"French\" },\r\n { id: \"fr-CA\", label: \"French (Canada)\" },\r\n { id: \"es\", label: \"Spanish\" },\r\n { id: \"es-ES\", label: \"Spanish (Spain)\" },\r\n { id: \"es-419\", label: \"Spanish (Latin America)\" },\r\n { id: \"pt-BR\", label: \"Portuguese (Brazil)\" },\r\n { id: \"pt-PT\", label: \"Portuguese (Portugal)\" },\r\n { id: \"it\", label: \"Italian\" },\r\n { id: \"pl\", label: \"Polish\" },\r\n { id: \"tr\", label: \"Turkish\" },\r\n { id: \"nl\", label: \"Dutch\" },\r\n { id: \"sv\", label: \"Swedish\" },\r\n { id: \"da\", label: \"Danish\" },\r\n { id: \"nb\", label: \"Norwegian Bokmal\" },\r\n { id: \"fi\", label: \"Finnish\" },\r\n { id: \"cs\", label: \"Czech\" },\r\n { id: \"sk\", label: \"Slovak\" },\r\n { id: \"hu\", label: \"Hungarian\" },\r\n { id: \"ro\", label: \"Romanian\" },\r\n { id: \"bg\", label: \"Bulgarian\" },\r\n { id: \"el\", label: \"Greek\" },\r\n { id: \"sr\", label: \"Serbian\" },\r\n { id: \"hr\", label: \"Croatian\" },\r\n { id: \"ar\", label: \"Arabic\" },\r\n { id: \"he\", label: \"Hebrew\" },\r\n { id: \"fa\", label: \"Persian\" },\r\n { id: \"hi\", label: \"Hindi\" },\r\n { id: \"th\", label: \"Thai\" },\r\n { id: \"vi\", label: \"Vietnamese\" },\r\n { id: \"id\", label: \"Indonesian\" },\r\n { id: \"ms\", label: \"Malay\" },\r\n { id: \"tl\", label: \"Filipino\" },\r\n { id: \"ja\", label: \"Japanese\" },\r\n { id: \"ko\", label: \"Korean\" },\r\n { id: \"zh-Hans\", label: \"Chinese (Simplified)\" },\r\n { id: \"zh-Hant\", label: \"Chinese (Traditional)\" },\r\n];\r\n\r\n/**\r\n * Порядок локалей для любого UI: base locale первой, остальные сохраняют порядок объявления.\r\n * Правило одно на весь редактор, поэтому и живёт оно здесь, а не копией в каждой панели.\r\n */\r\nexport function orderLocalesForDisplay(catalog: LocalizationCatalog): LocalizationLocale[] {\r\n return [\r\n ...catalog.locales.filter((locale) => locale.id === catalog.baseLocale),\r\n ...catalog.locales.filter((locale) => locale.id !== catalog.baseLocale),\r\n ];\r\n}\r\n\r\nexport const LOCALIZATION_DIRECTORY = \"locales\";\r\nexport const LOCALIZATION_COVERAGE_FILE = `${LOCALIZATION_DIRECTORY}/coverage.json`;\r\n/** Путь словаря внутри пакета. Безопасность сегмента гарантирует канонический ID. */\r\nexport function localeFilePath(localeId: string): string { return `${LOCALIZATION_DIRECTORY}/${localeId}.json`; }\r\n\r\nexport type LocalizationParams = Readonly<Record<string, string | number>>;\r\nconst PARAM_PATTERN = /\\{([A-Za-z0-9_.-]+)\\}/g;\r\n\r\n/** Имена runtime-параметров `{name}` внутри значения перевода, детерминированно отсортированные. */\r\nexport function collectLocalizationParams(value: string): string[] {\r\n return [...new Set([...value.matchAll(PARAM_PATTERN)].map((match) => match[1]!))].sort();\r\n}\r\n\r\n/** Неизвестный параметр остаётся видимым в фигурных скобках — это признак незаполненного значения. */\r\nexport function interpolateLocalizedText(value: string, params?: LocalizationParams): string {\r\n if (params === undefined) return value;\r\n return value.replace(PARAM_PATTERN, (match, name: string) => (Object.hasOwn(params, name) ? String(params[name]) : match));\r\n}\r\n\r\nexport type LocalizationTranslateContext = { key: string; locale: string; baseLocale: string; params?: LocalizationParams };\r\n/** Внешний переводчик потребителя. `undefined` означает «нет мнения» и не обрывает fallback. */\r\nexport type TranslateFn = (key: string, context: LocalizationTranslateContext) => string | undefined;\r\n\r\nexport type ResolveLocalizedTextInput = {\r\n /** Авторская строка `Text.text`: ручной режим и последний содержательный fallback. */\r\n text: string;\r\n textKey?: string;\r\n catalog?: LocalizationCatalog;\r\n locale?: string;\r\n translate?: TranslateFn;\r\n params?: LocalizationParams;\r\n};\r\n\r\n/** Откуда взялась итоговая строка. `literal` — у ноды нет ключа и локализация не участвовала вообще. */\r\nexport type LocalizedTextSource = \"literal\" | \"translate\" | \"locale\" | \"baseLocale\" | \"text\" | \"key\";\r\nexport type LocalizedTextResolution = { value: string; source: LocalizedTextSource };\r\n\r\n/**\r\n * Единственная fallback-цепочка: внешний `translate` → выбранная локаль → базовая локаль →\r\n * `Text.text` → сам ключ. Нода без `textKey` не обращается к локализации вообще, поэтому документ\r\n * без ключей выглядит ровно как до появления каталога.\r\n *\r\n * Возвращает и значение, и источник: редактору нужно показать, почему на макете именно эта строка,\r\n * и он обязан читать это из той же реализации, а не выводить цепочку заново.\r\n */\r\nexport function describeLocalizedText(input: ResolveLocalizedTextInput): LocalizedTextResolution {\r\n const key = input.textKey?.trim();\r\n if (key === undefined || key === \"\") return { value: input.text, source: \"literal\" };\r\n const baseLocale = input.catalog?.baseLocale ?? DEFAULT_LOCALE_ID;\r\n const locale = input.locale ?? baseLocale;\r\n const entry = input.catalog?.entries[key];\r\n const external = input.translate?.(key, { key, locale, baseLocale, params: input.params });\r\n const resolution: LocalizedTextResolution = external !== undefined\r\n ? { value: external, source: \"translate\" }\r\n : entry !== undefined && Object.hasOwn(entry, locale) ? { value: entry[locale]!, source: \"locale\" }\r\n : entry !== undefined && Object.hasOwn(entry, baseLocale) ? { value: entry[baseLocale]!, source: \"baseLocale\" }\r\n : input.text.length > 0 ? { value: input.text, source: \"text\" } : { value: key, source: \"key\" };\r\n return { value: interpolateLocalizedText(resolution.value, input.params), source: resolution.source };\r\n}\r\n\r\nexport function resolveLocalizedText(input: ResolveLocalizedTextInput): string {\r\n return describeLocalizedText(input).value;\r\n}\r\n\r\n/**\r\n * `Text.textCase` display transform: applied last, on top of whatever `describeLocalizedText`\r\n * resolved (authored string, live preview, or translation) — never written back into `text` or a\r\n * catalog entry, so the source string and the case choice stay independently editable. Omitted/`\"none\"`\r\n * is a no-op, keeping every existing document's rendered output unchanged.\r\n */\r\nexport function applyTextCase(text: string, textCase: TextCase | undefined): string {\r\n switch (textCase) {\r\n case \"uppercase\": return text.toUpperCase();\r\n case \"lowercase\": return text.toLowerCase();\r\n default: return text;\r\n }\r\n}\r\n\r\n/** Владелец иерархии, в которой найдена ссылка: сцена или определение пресета. */\r\nexport type LocalizationReferenceOwner = { kind: \"scene\"; id: string } | { kind: \"prefab\"; id: string };\r\nexport type LocalizationKeyReference = {\r\n key: string;\r\n path: string;\r\n owner: LocalizationReferenceOwner;\r\n /**\r\n * Адрес узла в терминах редактора: собственный id персистентного/локального узла либо\r\n * materialized-адрес `instanceId::prefab-node::nodePath` для property override.\r\n */\r\n nodeId: string;\r\n};\r\n/** Ссылка вместе с записью значения: одна и та же реализация обхода читает и переименовывает ключи. */\r\nexport type LocalizationKeyReferenceSite = LocalizationKeyReference & { setKey(next: string): void };\r\n\r\n/**\r\n * Все ссылки `Text.textKey` документа: сцены, определения пресетов, локально добавленные узлы любой\r\n * вложенности и persisted property overrides. Унаследованные узлы инстанса ключ не «размножают»:\r\n * их источником является узел определения, уже посчитанный обходом `document.prefabs`.\r\n *\r\n * Обход единственный: `collectTextKeyReferences` только собирает результат, а rename каталога\r\n * переписывает ключи через `setKey`, поэтому «где живут ссылки» описано ровно в одном месте.\r\n */\r\nexport function forEachTextKeyReference(document: ProjectDocument, visit: (site: LocalizationKeyReferenceSite) => void): void {\r\n const visitNode = (node: UINode, path: string, owner: LocalizationReferenceOwner): void => {\r\n if (node.type === \"text\" && node.textKey !== undefined) {\r\n visit({ key: node.textKey, path: `${path}/textKey`, owner, nodeId: node.id, setKey: (next) => { node.textKey = next; } });\r\n }\r\n if (node.type !== \"prefab-instance\") return;\r\n (node.propertyOverrides ?? []).forEach((override, index) => {\r\n const value = override.patch.textKey;\r\n if (typeof value !== \"string\") return;\r\n visit({\r\n key: value,\r\n path: `${path}/propertyOverrides/${index}/patch/textKey`,\r\n owner,\r\n nodeId: materializedPrefabNodeId(node.id, override.nodeId),\r\n setKey: (next) => { override.patch.textKey = next; },\r\n });\r\n });\r\n (node.locallyAddedNodes ?? []).forEach((local, index) => visitNode(local, `${path}/locallyAddedNodes/${index}`, owner));\r\n };\r\n document.scenes.forEach((scene, sceneIndex) => scene.nodes.forEach((node, nodeIndex) => visitNode(node, `/scenes/${sceneIndex}/nodes/${nodeIndex}`, { kind: \"scene\", id: scene.id })));\r\n document.prefabs.forEach((prefab, prefabIndex) => prefab.nodes.forEach((node, nodeIndex) => visitNode(node, `/prefabs/${prefabIndex}/nodes/${nodeIndex}`, { kind: \"prefab\", id: prefab.id })));\r\n}\r\n\r\nexport function collectTextKeyReferences(document: ProjectDocument): LocalizationKeyReference[] {\r\n const references: LocalizationKeyReference[] = [];\r\n forEachTextKeyReference(document, ({ key, path, owner, nodeId }) => references.push({ key, path, owner, nodeId }));\r\n return references;\r\n}\r\n\r\nexport type LocalizationLocaleCoverage = { id: string; label: string; isBase: boolean; translated: number; missingKeys: string[] };\r\nexport type LocalizationCoverage = {\r\n baseLocale: string;\r\n /** Все ключи каталога — покрытие считается по каталогу, а не по текущему usage. */\r\n keys: string[];\r\n keyCount: number;\r\n locales: LocalizationLocaleCoverage[];\r\n usedKeys: string[];\r\n unusedKeys: string[];\r\n /** Ключи, на которые ссылается `Text`, но которых нет в каталоге. */\r\n missingKeys: string[];\r\n};\r\n\r\n/** Детерминированное покрытие: и ключи, и локали, и списки отсортированы. */\r\nexport function computeLocalizationCoverage(document: ProjectDocument): LocalizationCoverage {\r\n const catalog = document.localization;\r\n const keys = Object.keys(catalog.entries).sort();\r\n const referenced = new Set(collectTextKeyReferences(document).map((reference) => reference.key.trim()).filter((key) => key !== \"\"));\r\n const usedKeys = keys.filter((key) => referenced.has(key));\r\n return {\r\n baseLocale: catalog.baseLocale,\r\n keys,\r\n keyCount: keys.length,\r\n locales: catalog.locales.map((locale) => {\r\n const missingKeys = keys.filter((key) => !Object.hasOwn(catalog.entries[key]!, locale.id));\r\n return { id: locale.id, label: locale.label, isBase: locale.id === catalog.baseLocale, translated: keys.length - missingKeys.length, missingKeys };\r\n }),\r\n usedKeys,\r\n unusedKeys: keys.filter((key) => !referenced.has(key)),\r\n missingKeys: [...referenced].filter((key) => !Object.hasOwn(catalog.entries, key)).sort(),\r\n };\r\n}\r\n\r\nconst issue = (code: string, path: string, message: string, severity: ValidationIssue[\"severity\"]): ValidationIssue => ({ code, path, message, severity });\r\n\r\n/**\r\n * Structural/semantic ошибки каталога: они делают документ невалидным.\r\n *\r\n * Здесь только то, что нельзя доставить ни при каком заполнении переводов: неканоничный или\r\n * дублирующийся locale ID, неизвестная base locale и пустая ссылка на ключ.\r\n */\r\nexport function collectLocalizationErrors(document: ProjectDocument): ValidationIssue[] {\r\n const issues: ValidationIssue[] = [];\r\n const catalog = document.localization;\r\n const seen = new Set<string>();\r\n catalog.locales.forEach((locale, index) => {\r\n const path = `/localization/locales/${index}/id`;\r\n if (!isCanonicalLocaleId(locale.id)) issues.push(issue(\"INVALID_LOCALE_ID\", path, `Locale '${locale.id}' is not a canonical BCP-47 identifier.`, \"error\"));\r\n if (seen.has(locale.id)) issues.push(issue(\"DUPLICATE_LOCALE\", path, `Locale '${locale.id}' is declared more than once.`, \"error\"));\r\n seen.add(locale.id);\r\n });\r\n if (!seen.has(catalog.baseLocale)) issues.push(issue(\"UNKNOWN_BASE_LOCALE\", \"/localization/baseLocale\", `Base locale '${catalog.baseLocale}' is not declared.`, \"error\"));\r\n for (const key of Object.keys(catalog.entries)) {\r\n if (key.trim() === \"\") issues.push(issue(\"EMPTY_LOCALIZATION_KEY\", `/localization/entries/${key}`, \"A localization key must not be blank.\", \"error\"));\r\n }\r\n for (const reference of collectTextKeyReferences(document)) {\r\n if (reference.key.trim() === \"\") issues.push(issue(\"EMPTY_LOCALIZATION_KEY\", reference.path, \"A textKey reference must not be blank.\", \"error\"));\r\n }\r\n return issues;\r\n}\r\n\r\n/**\r\n * Authoring diagnostics: каталог заполняется постепенно, поэтому всё это — warnings, и документ с\r\n * ними остаётся валидным и сохраняемым.\r\n */\r\nexport function collectLocalizationWarnings(document: ProjectDocument): ValidationIssue[] {\r\n const issues: ValidationIssue[] = [];\r\n const catalog = document.localization;\r\n const coverage = computeLocalizationCoverage(document);\r\n const declared = new Set(catalog.locales.map((locale) => locale.id));\r\n for (const reference of collectTextKeyReferences(document)) {\r\n const key = reference.key.trim();\r\n if (key !== \"\" && !Object.hasOwn(catalog.entries, key)) issues.push(issue(\"MISSING_LOCALIZATION_ENTRY\", reference.path, `Localization key '${key}' is not in the catalog.`, \"warning\"));\r\n }\r\n for (const key of coverage.keys) {\r\n const entry = catalog.entries[key]!;\r\n if (!Object.hasOwn(entry, catalog.baseLocale)) issues.push(issue(\"MISSING_BASE_TRANSLATION\", `/localization/entries/${key}`, `Key '${key}' has no '${catalog.baseLocale}' translation.`, \"warning\"));\r\n const baseParams = Object.hasOwn(entry, catalog.baseLocale) ? collectLocalizationParams(entry[catalog.baseLocale]!).join(\",\") : undefined;\r\n for (const localeId of Object.keys(entry).sort()) {\r\n if (!declared.has(localeId)) { issues.push(issue(\"UNKNOWN_TRANSLATION_LOCALE\", `/localization/entries/${key}/${localeId}`, `Locale '${localeId}' is not declared and will not be exported.`, \"warning\")); continue; }\r\n if (baseParams === undefined || localeId === catalog.baseLocale) continue;\r\n if (collectLocalizationParams(entry[localeId]!).join(\",\") !== baseParams) issues.push(issue(\"PARAM_MISMATCH\", `/localization/entries/${key}/${localeId}`, `Translation '${key}' for '${localeId}' uses different runtime parameters than the base locale.`, \"warning\"));\r\n }\r\n }\r\n for (const locale of coverage.locales) {\r\n if (locale.isBase || locale.missingKeys.length === 0) continue;\r\n issues.push(issue(\"INCOMPLETE_LOCALE\", `/localization/locales/${catalog.locales.findIndex((candidate) => candidate.id === locale.id)}`, `Locale '${locale.id}' is missing ${locale.missingKeys.length} translation(s).`, \"warning\"));\r\n }\r\n for (const key of coverage.unusedKeys) {\r\n issues.push(issue(\"UNUSED_LOCALIZATION_KEY\", `/localization/entries/${key}`, `Key '${key}' is not referenced by any delivered Text node.`, \"warning\"));\r\n }\r\n return issues;\r\n}\r\n\r\nexport function collectLocalizationIssues(document: ProjectDocument): ValidationIssue[] {\r\n return [...collectLocalizationErrors(document), ...collectLocalizationWarnings(document)];\r\n}\r\n\r\n/**\r\n * Строгая export-проверка: то же состояние, что в авторинге является warning, для пакета является\r\n * блокирующей ошибкой, если ключ каталога вообще нельзя доставить — у него нет base translation.\r\n * Неполные secondary locales и unused keys экспорт не блокируют и живут в coverage-отчёте.\r\n */\r\nexport function collectLocalizationExportErrors(document: ProjectDocument): ValidationIssue[] {\r\n const catalog = document.localization;\r\n const issues = collectLocalizationErrors(document);\r\n for (const key of Object.keys(catalog.entries).sort()) {\r\n if (!Object.hasOwn(catalog.entries[key]!, catalog.baseLocale)) issues.push(issue(\"MISSING_BASE_TRANSLATION\", `/localization/entries/${key}`, `Key '${key}' cannot be delivered: it has no '${catalog.baseLocale}' translation.`, \"error\"));\r\n }\r\n return issues;\r\n}\r\n\r\n/**\r\n * Словарь одной локали: плоский `Record<string, string>` без metadata wrapper, готовый любому\r\n * внешнему i18n. Ключ попадает в файл только если у него есть значение именно этой локали;\r\n * отсутствующий перевод не превращается в `\"\"`, а остаётся отмеченным в coverage.\r\n */\r\nexport function buildLocaleDictionary(catalog: LocalizationCatalog, localeId: string): Record<string, string> {\r\n const dictionary: Record<string, string> = {};\r\n for (const key of Object.keys(catalog.entries).sort()) {\r\n const entry = catalog.entries[key]!;\r\n if (Object.hasOwn(entry, localeId)) dictionary[key] = entry[localeId]!;\r\n }\r\n return dictionary;\r\n}\r\n\r\nexport function buildLocaleDictionaries(catalog: LocalizationCatalog): Record<string, Record<string, string>> {\r\n return Object.fromEntries(catalog.locales.map((locale) => [locale.id, buildLocaleDictionary(catalog, locale.id)]));\r\n}\r\n\r\nexport type LocalizationCoverageReport = LocalizationCoverage & { warnings: ValidationIssue[] };\r\n/** Отчёт покрытия пакета: он дополняет словари, а не заменяет их. */\r\nexport function buildLocalizationCoverageReport(document: ProjectDocument): LocalizationCoverageReport {\r\n return { ...computeLocalizationCoverage(document), warnings: collectLocalizationWarnings(document) };\r\n}\r\n\r\n/**\r\n * Localization-часть пакета: по одному плоскому словарю на каждую объявленную локаль плюс отчёт\r\n * покрытия. Каталог экспортируется целиком: unused-ключ может быть контрактом игрового кода, а\r\n * usage текущей иерархии не доказывает, что он не нужен.\r\n *\r\n * Раскладка описана здесь и только здесь: и ZIP-экспорт редактора, и фикстура демо обязаны звать\r\n * эту функцию, иначе демо проверяло бы не тот контракт. Документ ожидается уже подготовленным к\r\n * доставке (без `editorOnly` поддеревьев), потому что coverage говорит о доставляемых `Text`.\r\n */\r\nexport function collectPackageLocaleEntries(document: ProjectDocument): Record<string, string> {\r\n const entries: Record<string, string> = {};\r\n for (const locale of document.localization.locales) {\r\n entries[localeFilePath(locale.id)] = `${JSON.stringify(buildLocaleDictionary(document.localization, locale.id), null, 2)}\\n`;\r\n }\r\n entries[LOCALIZATION_COVERAGE_FILE] = `${JSON.stringify(buildLocalizationCoverageReport(document), null, 2)}\\n`;\r\n return entries;\r\n}\r\n\r\n/**\r\n * Блокировка экспорта каталогом. Сообщение формируется ровно здесь, чтобы UI показывал ту же\r\n * диагностику, что попадает в консоль, а не собственную формулировку.\r\n */\r\nexport class LocalizationExportError extends Error {\r\n readonly issues: ValidationIssue[];\r\n\r\n constructor(issues: ValidationIssue[]) {\r\n super(`The localization catalog cannot be packaged:\\n${issues.map((item) => `• ${item.message} (${item.code} at ${item.path})`).join(\"\\n\")}`);\r\n this.name = \"LocalizationExportError\";\r\n this.issues = issues;\r\n }\r\n}\r\n\r\n/**\r\n * Строгий pre-export gate. То, что в авторинге остаётся warning, здесь блокирует пакет, если ключ\r\n * невозможно доставить вообще: у него нет перевода базовой локали. Неполные secondary locales и\r\n * unused keys экспорт не останавливают и попадают только в `locales/coverage.json`.\r\n */\r\nexport function assertExportableLocalization(document: ProjectDocument): void {\r\n const errors = collectLocalizationExportErrors(document);\r\n if (errors.length > 0) throw new LocalizationExportError(errors);\r\n}\r\n"]}
|