poe-svelte-ui-lib 1.45.0 → 1.46.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/README.md CHANGED
@@ -110,6 +110,8 @@ npm install poe-svelte-ui-lib
110
110
 
111
111
  **README компонента** (`src/lib/<Компонент>/README.md`, вкладка «Инфо» витрины) — развёрнутая документация для разработчика. Первые разделы повторяют описание (i) и не должны ему противоречить, дальше идут разработческие: `## Назначение` (плюс заметка о размерах) → `## Пакет данных` (плюс `### Протокол устройства` / пример кода прошивки) → `## Ключи` (таблица «Ключ / Описание», те же формулировки, что в (i)) → `## Особенности` (поведение, ограничения) → `## Пропсы` (таблица «Название / Тип / По умолчанию / Описание», колбэки — `### Колбэки`) → `## Примеры` (Svelte) → `## Конструктор свойств (<Компонент>Props.svelte)` → `## Внутреннее устройство`.
112
112
 
113
+ **Языки — единственный источник в библиотеке.** Список языков — `LANGUAGES` в `locales/languages.ts` (код и название, без Svelte — годится и для серверного кода); из него выводятся `LanguageCode`, `LANGUAGE_CODES`, `isLanguageCode` и тип строки словаря `Translation` (по переводу на каждый язык в порядке `LANGUAGES`, TypeScript не даст пропустить язык). `locales/i18n.ts` — текущий язык `Language`/`setLanguage` (сохраняется в `localStorage` `AppLanguage`), `LOCALES` для переключателей (с флагами) и механизм перевода `createTranslator(словарь)`. Приложения (DevCloud, WebApp, терминал) свой список языков, хранилище и функцию перевода не держат: берут `Language`, `setLanguage`, `LOCALES` из библиотеки, а свой `$T` строят из своего словаря — `export const T = createTranslator(translations)`, где `translations: Translations`. Одно `setLanguage` переключает и компоненты библиотеки, и приложение. Новый язык — строка в `LANGUAGES` и флаг в `i18n.ts`, затем TypeScript покажет все словари, где не хватает перевода.
114
+
113
115
  **Языки (ru/en/zh) — два слоя:**
114
116
 
115
117
  - **Встроенные тексты компонентов** (подписи полей виджетов, кнопки, подсказки, `aria-label`) — только через словарь `src/lib/locales/translations.ts` и `$T("ключ")`; строка с числами — параметрами: `$T("widget.fs.files", { n: files.length })` на ключ `"Файлов: {n}"`. Русских литералов в разметке и скриптах компонентов быть не должно. Списки вариантов с подписями — `$derived`, иначе не переключатся при смене языка. Исключение — шкалы прибора `WidgetPFD`: там общепринятые авиационные сокращения (`SPD`, `ALT`, `AS`, `VS`, `NO DATA`) на всех языках, переводятся только подсказки и строки состояния.
@@ -3,7 +3,7 @@
3
3
  import { getContext } from "svelte"
4
4
  import { T } from "../locales/i18n"
5
5
  import translations from "../locales/translations"
6
- import { LANGUAGE_CODES } from "../locales/localize"
6
+ import { LANGUAGE_CODES } from "../locales/languages"
7
7
  import type { IInputProps, UIComponent, IOption, IUIComponentHandler } from "../types"
8
8
  import * as UI from ".."
9
9
  import { optionsStore, findColorOption } from "../options"
@@ -3,8 +3,8 @@
3
3
  поэтому окно одно на все компоненты; окна свойств сами тексты не показывают и не правят. -->
4
4
  <script lang="ts">
5
5
  import { T } from "./locales/i18n"
6
- import { LANGUAGE_CODES, collectTexts, packLocalized, setAtPath, type TextPath } from "./locales/localize"
7
- import type { LanguageCode } from "./locales/translations"
6
+ import { collectTexts, packLocalized, setAtPath, type TextPath } from "./locales/localize"
7
+ import { LANGUAGE_CODES, type LanguageCode } from "./locales/languages"
8
8
  import Button from "./Button/Button.svelte"
9
9
  import Input from "./Input/Input.svelte"
10
10
  import Modal from "./Modal.svelte"
package/dist/index.d.ts CHANGED
@@ -51,7 +51,7 @@ export { default as WidgetPFDProps } from "./WidgetPFD/WidgetPFDProps.svelte";
51
51
  export { default as WidgetMap } from "./WidgetMap/WidgetMap.svelte";
52
52
  export { default as WidgetMapProps } from "./WidgetMap/WidgetMapProps.svelte";
53
53
  export * from "./locales/i18n";
54
- export * from "./locales/translations";
54
+ export * from "./locales/languages";
55
55
  export * from "./locales/localize";
56
56
  export { widgetAccentStyle } from "./widgetAccent";
57
57
  export * from "./ModalStackStore";
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ export { default as WidgetPFDProps } from "./WidgetPFD/WidgetPFDProps.svelte";
52
52
  export { default as WidgetMap } from "./WidgetMap/WidgetMap.svelte";
53
53
  export { default as WidgetMapProps } from "./WidgetMap/WidgetMapProps.svelte";
54
54
  export * from "./locales/i18n";
55
- export * from "./locales/translations";
55
+ export * from "./locales/languages";
56
56
  export * from "./locales/localize";
57
57
  export { widgetAccentStyle } from "./widgetAccent";
58
58
  export * from "./ModalStackStore";
@@ -1,11 +1,14 @@
1
1
  import { type Readable } from "svelte/store";
2
+ import { type LanguageCode, type Translations } from "./languages";
2
3
  export declare const Language: import("svelte/store").Writable<string>;
3
4
  export declare function setLanguage(newLang: string): void;
4
5
  export declare const LOCALES: {
5
6
  id: string;
6
7
  name: string;
7
- value: string;
8
+ value: LanguageCode;
8
9
  component: ConstructorOfATypedSvelteComponent;
9
10
  }[];
10
11
  export type TranslateParams = Record<string, string | number>;
11
- export declare const T: Readable<(key: string, params?: TranslateParams) => string>;
12
+ export type Translator = (key: string, params?: TranslateParams) => string;
13
+ export declare const createTranslator: (dictionary: Translations) => Readable<Translator>;
14
+ export declare const T: Readable<Translator>;
@@ -1,39 +1,58 @@
1
- import { derived } from "svelte/store";
2
- import { writable } from "svelte/store";
1
+ import { derived, writable } from "svelte/store";
3
2
  import translations from "./translations";
4
3
  import FlagZh from "./FlagZh.svelte";
5
4
  import FlagRu from "./FlagRu.svelte";
6
5
  import FlagEn from "./FlagEn.svelte";
7
- /* Язык по умолчанию */
8
- let initialLanguage = "ru";
9
- /* Проверяем, доступен ли localStorage */
6
+ import { DEFAULT_LANGUAGE, LANGUAGES, isLanguageCode } from "./languages";
7
+ /* Язык и механизм перевода — общие для библиотеки и приложений: приложение берёт отсюда Language/setLanguage/LOCALES и строит свой $T
8
+ из своего словаря через createTranslator (см. README «Языки»); переключение языка одним setLanguage меняет и компоненты, и приложение */
9
+ /* Стартовый язык: сохранённый → язык браузера → по умолчанию */
10
+ let initialLanguage = DEFAULT_LANGUAGE;
10
11
  if (typeof window !== "undefined") {
11
- initialLanguage = localStorage.getItem("AppLanguage") || navigator.language?.split("-")[0]?.toLowerCase() || "ru";
12
- const supported = ["ru", "en", "zh"];
13
- if (!supported.includes(initialLanguage)) {
14
- initialLanguage = "ru";
12
+ let saved = null;
13
+ try {
14
+ saved = localStorage.getItem("AppLanguage");
15
15
  }
16
+ catch {
17
+ /* хранилище недоступно */
18
+ }
19
+ const candidate = saved || navigator.language?.split("-")[0]?.toLowerCase();
20
+ if (isLanguageCode(candidate))
21
+ initialLanguage = candidate;
16
22
  }
17
- /* Создаем хранилище с начальным значением языка */
23
+ /* Текущий язык */
18
24
  export const Language = writable(initialLanguage);
19
- /* Функция для изменения языка */
25
+ /* Смена языка с сохранением выбора */
20
26
  export function setLanguage(newLang) {
27
+ if (!isLanguageCode(newLang))
28
+ return;
21
29
  if (typeof window !== "undefined") {
22
- localStorage.setItem("AppLanguage", newLang);
30
+ try {
31
+ localStorage.setItem("AppLanguage", newLang);
32
+ }
33
+ catch {
34
+ /* хранилище недоступно — выбор действует до перезагрузки */
35
+ }
23
36
  }
24
37
  Language.set(newLang);
25
38
  }
26
- /* Поддерживаемые языки (код, название языка, флаг) */
27
- export const LOCALES = [
28
- { id: "app-language-ru", name: "Русский", value: "ru", component: FlagRu },
29
- { id: "app-language-en", name: "English", value: "en", component: FlagEn },
30
- { id: "app-language-zh", name: "中国人", value: "zh", component: FlagZh },
31
- ];
32
- /* Индекс языка в кортеже перевода [ru, en, zh] — жёстко привязан к порядку LOCALES */
33
- const LANGUAGE_INDEX = Object.fromEntries(LOCALES.map((locale, index) => [locale.value, index]));
34
- /* Функция для перевода для текущего языка */
35
- function translate(locale, key, params) {
36
- const text = translations[key]?.[LANGUAGE_INDEX[locale] ?? 0] || key;
39
+ /* Языки для переключателей: список из languages.ts + флаг */
40
+ const FLAGS = { ru: FlagRu, en: FlagEn, zh: FlagZh };
41
+ export const LOCALES = LANGUAGES.map((language) => ({
42
+ id: `app-language-${language.value}`,
43
+ name: language.name,
44
+ value: language.value,
45
+ component: FLAGS[language.value],
46
+ }));
47
+ /* Индекс языка в строке словаря — порядок LANGUAGES */
48
+ const LANGUAGE_INDEX = Object.fromEntries(LANGUAGES.map((language, index) => [language.value, index]));
49
+ /* Перевод по словарю: текущий язык → русский → сам ключ */
50
+ const translate = (dictionary, locale, key, params) => {
51
+ const row = dictionary[key];
52
+ const text = row?.[LANGUAGE_INDEX[locale] ?? 0] || row?.[0] || key;
37
53
  return params ? text.replace(/\{(\w+)\}/g, (match, name) => (name in params ? String(params[name]) : match)) : text;
38
- }
39
- export const T = derived(Language, ($lang) => (key, params) => translate($lang, key, params));
54
+ };
55
+ /* $T приложения из его собственного словаря — поверх общего языка */
56
+ export const createTranslator = (dictionary) => derived(Language, ($lang) => (key, params) => translate(dictionary, $lang, key, params));
57
+ /* $T самой библиотеки */
58
+ export const T = createTranslator(translations);
@@ -0,0 +1,20 @@
1
+ export declare const LANGUAGES: readonly [{
2
+ readonly value: "ru";
3
+ readonly name: "Русский";
4
+ }, {
5
+ readonly value: "en";
6
+ readonly name: "English";
7
+ }, {
8
+ readonly value: "zh";
9
+ readonly name: "中文";
10
+ }];
11
+ export type LanguageCode = (typeof LANGUAGES)[number]["value"];
12
+ export declare const LANGUAGE_CODES: LanguageCode[];
13
+ type StringPerItem<T extends readonly unknown[]> = {
14
+ -readonly [K in keyof T]: string;
15
+ };
16
+ export type Translation = StringPerItem<typeof LANGUAGES>;
17
+ export type Translations = Record<string, Translation>;
18
+ export declare const DEFAULT_LANGUAGE: LanguageCode;
19
+ export declare const isLanguageCode: (value: unknown) => value is LanguageCode;
20
+ export {};
@@ -0,0 +1,11 @@
1
+ /* $lib/locales/languages.ts */
2
+ /* Единственный источник списка языков для библиотеки и всех приложений на ней (DevCloud, WebApp, терминал); без Svelte-зависимостей — годится и для серверного кода */
3
+ export const LANGUAGES = [
4
+ { value: "ru", name: "Русский" },
5
+ { value: "en", name: "English" },
6
+ { value: "zh", name: "中文" },
7
+ ];
8
+ /* Коды в порядке LANGUAGES — он же порядок переводов в строке словаря */
9
+ export const LANGUAGE_CODES = LANGUAGES.map((language) => language.value);
10
+ export const DEFAULT_LANGUAGE = "ru";
11
+ export const isLanguageCode = (value) => LANGUAGE_CODES.includes(value);
@@ -1,6 +1,5 @@
1
- import type { LanguageCode } from "./translations";
1
+ import { type LanguageCode } from "./languages";
2
2
  export type LocalizedText = string | Partial<Record<LanguageCode, string>>;
3
- export declare const LANGUAGE_CODES: LanguageCode[];
4
3
  export declare const isLocalizedText: (value: unknown) => value is Partial<Record<LanguageCode, string>>;
5
4
  export declare const localizeText: (value: LocalizedText | undefined, lang: string) => string;
6
5
  export declare function localizeDeep<T>(value: T, lang: string): T;
@@ -1,4 +1,6 @@
1
- export const LANGUAGE_CODES = ["ru", "en", "zh"];
1
+ /* $lib/locales/localize.ts */
2
+ /* Многоязычные тексты, введённые в конструкторе: строка (один язык) или объект { ru, en, zh }; ru — основной и запасной */
3
+ import { LANGUAGE_CODES } from "./languages";
2
4
  /* Поля свойств, которые считаются текстом для пользователя — их показывает и правит окно «Тексты и переводы» (TextsEditor) */
3
5
  const TEXT_KEYS = new Set([
4
6
  "name",
@@ -1,3 +1,3 @@
1
- export type LanguageCode = "ru" | "en" | "zh";
2
- declare const translations: Record<string, [string, string, string]>;
1
+ import type { Translations } from "./languages";
2
+ declare const translations: Translations;
3
3
  export default translations;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.45.0",
3
+ "version": "1.46.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Oleg-Perevyshin/ElementsUI.git"