@djangocfg/widget-media 0.1.8 → 0.1.12

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
@@ -21,7 +21,7 @@ and a host mounting a lightbox must not pay for any of them.
21
21
  | `./diagram` | `LazyDiagram`, `DiagramSource`, `diagramPlugin` | mermaid (~2.5 MB) — **always deferred** |
22
22
  | `./markdown` | `Markdown` — for a reader, outside any conversation | streamdown |
23
23
  | `./code` | `Code` — a read-only listing | streamdown + shiki |
24
- | `./i18n` | `MediaCopy`, `DEFAULT_MEDIA_COPY` | — |
24
+ | `./i18n` | `MediaCopy`, `DEFAULT_MEDIA_COPY`, `MEDIA_DICTIONARIES`, `useMediaCopy` | — |
25
25
 
26
26
  ```tsx
27
27
  import { ImageViewer } from '@djangocfg/widget-media';
@@ -33,8 +33,10 @@ import { LazyDiagram } from '@djangocfg/widget-media/diagram';
33
33
 
34
34
  ## Copy
35
35
 
36
- The package owns its copy, typed. Pass `copy` to override any subset; keys you
37
- omit fall back to English, so translating one label does not blank the rest.
36
+ The package owns its copy, typed, in the 17 `@djangocfg/i18n` locales. The
37
+ language comes from the `@djangocfg/i18n` provider (English without one). Pass
38
+ `copy` to override any subset; keys you omit keep the dictionary's text, so
39
+ changing one label does not blank the rest.
38
40
 
39
41
  ```tsx
40
42
  <ImageViewer images={images} copy={{ zoomIn: 'Приблизить', close: 'Закрыть' }} />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/widget-media",
3
- "version": "0.1.8",
3
+ "version": "0.1.12",
4
4
  "description": "Media viewers for a conversation or a file preview: zoom/pan image lightbox, mermaid diagrams, markdown and code listings",
5
5
  "keywords": [
6
6
  "image-viewer",
@@ -56,14 +56,16 @@
56
56
  "zustand": "^5.0.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@djangocfg/ui-core": "^2.1.549",
59
+ "@djangocfg/i18n": "^2.1.586",
60
+ "@djangocfg/ui-core": "^2.1.586",
60
61
  "lucide-react": "^0.545.0",
61
62
  "react": "^19.0.0",
62
63
  "streamdown": "^2.6.0"
63
64
  },
64
65
  "devDependencies": {
65
- "@djangocfg/typescript-config": "^2.1.549",
66
- "@djangocfg/ui-core": "^2.1.549",
66
+ "@djangocfg/i18n": "^2.1.586",
67
+ "@djangocfg/typescript-config": "^2.1.586",
68
+ "@djangocfg/ui-core": "^2.1.586",
67
69
  "@storybook/react-vite": "^10.5.0",
68
70
  "@types/node": "^25.9.5",
69
71
  "@types/react": "19.2.15",
package/src/i18n/index.ts CHANGED
@@ -1,60 +1,49 @@
1
1
  'use client';
2
2
 
3
- /**
4
- * The package owns its copy, typed.
5
- *
6
- * `MediaCopy` is the schema of record: a missing or misspelled key is a `tsc`
7
- * error rather than `undefined` rendered into a toolbar button. Copy used to
8
- * arrive through a host i18n provider, which made the viewer unmountable
9
- * without one — a story, a preview route and a test each had to stand a
10
- * provider up to draw one image.
11
- *
12
- * A host that already translates passes `copy` to override any subset; keys it
13
- * omits fall back to English.
14
- */
3
+ import { useMemo } from 'react';
15
4
 
16
- export interface MediaCopy {
17
- close: string;
18
- diagram: string;
19
- download: string;
20
- failedToLoad: string;
21
- fitToView: string;
22
- flipHorizontal: string;
23
- flipVertical: string;
24
- fullscreen: string;
25
- loading: string;
26
- next: string;
27
- noImage: string;
28
- previous: string;
29
- rotate: string;
30
- zoomIn: string;
31
- zoomOut: string;
32
- }
5
+ import { createCopy, type Locale } from '@djangocfg/i18n/copy';
6
+
7
+ import { ar } from './locales/ar';
8
+ import { da } from './locales/da';
9
+ import { de } from './locales/de';
10
+ import { en } from './locales/en';
11
+ import { es } from './locales/es';
12
+ import { fr } from './locales/fr';
13
+ import { it } from './locales/it';
14
+ import { ja } from './locales/ja';
15
+ import { ko } from './locales/ko';
16
+ import { nl } from './locales/nl';
17
+ import { no } from './locales/no';
18
+ import { pl } from './locales/pl';
19
+ import { ptBR } from './locales/ptBR';
20
+ import { ru } from './locales/ru';
21
+ import { sv } from './locales/sv';
22
+ import { tr } from './locales/tr';
23
+ import { zh } from './locales/zh';
33
24
 
34
- export const DEFAULT_MEDIA_COPY: MediaCopy = {
35
- close: 'Close',
36
- diagram: 'Diagram',
37
- download: 'Download',
38
- failedToLoad: 'Failed to load',
39
- fitToView: 'Fit to view',
40
- flipHorizontal: 'Flip horizontal',
41
- flipVertical: 'Flip vertical',
42
- fullscreen: 'Fullscreen',
43
- loading: 'Loading…',
44
- next: 'Next',
45
- noImage: 'No image',
46
- previous: 'Previous',
47
- rotate: 'Rotate',
48
- zoomIn: 'Zoom in',
49
- zoomOut: 'Zoom out',
25
+ import type { MediaCopy } from './types';
26
+
27
+ export type { MediaCopy } from './types';
28
+
29
+ /** Every bundled dictionary, typed complete over the shared `Locale` union. */
30
+ export const MEDIA_DICTIONARIES: Readonly<Record<Locale, MediaCopy>> = {
31
+ en, ru, ko, ja, de, fr, zh, it, es, nl, ar, tr, pl, sv, no, da,
32
+ 'pt-BR': ptBR,
50
33
  };
51
34
 
35
+ /** English, for a host that wants the unlocalised defaults. */
36
+ export const DEFAULT_MEDIA_COPY: MediaCopy = en;
37
+
38
+ const kit = createCopy(MEDIA_DICTIONARIES);
39
+
52
40
  /**
53
- * Resolve the copy a component renders. Takes the host's partial override and
54
- * fills the rest from the defaults, so a caller supplying one string does not
55
- * blank the other fourteen.
41
+ * The copy a component renders: the dictionary for the active language (the
42
+ * `locale` argument, else the `@djangocfg/i18n` provider, else English), with
43
+ * the host's partial override on top. A host that overrides one string does
44
+ * not blank the other fourteen.
56
45
  */
57
- export function useMediaCopy(copy?: Partial<MediaCopy>): MediaCopy {
58
- if (!copy) return DEFAULT_MEDIA_COPY;
59
- return { ...DEFAULT_MEDIA_COPY, ...copy };
46
+ export function useMediaCopy(copy?: Partial<MediaCopy>, locale?: string): MediaCopy {
47
+ const base = kit.useCopy(locale);
48
+ return useMemo(() => (copy ? { ...base, ...copy } : base), [base, copy]);
60
49
  }
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const ar: MediaCopy = {
4
+ close: 'إغلاق',
5
+ diagram: 'مخطط',
6
+ download: 'تنزيل',
7
+ failedToLoad: 'فشل التحميل',
8
+ fitToView: 'ملاءمة للنافذة',
9
+ flipHorizontal: 'قلب أفقي',
10
+ flipVertical: 'قلب عمودي',
11
+ fullscreen: 'ملء الشاشة',
12
+ loading: 'جارٍ التحميل…',
13
+ next: 'التالي',
14
+ noImage: 'لا توجد صورة',
15
+ previous: 'السابق',
16
+ rotate: 'تدوير',
17
+ zoomIn: 'تكبير',
18
+ zoomOut: 'تصغير',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const da: MediaCopy = {
4
+ close: 'Luk',
5
+ diagram: 'Diagram',
6
+ download: 'Download',
7
+ failedToLoad: 'Kunne ikke indlæses',
8
+ fitToView: 'Tilpas til vindue',
9
+ flipHorizontal: 'Spejlvend vandret',
10
+ flipVertical: 'Spejlvend lodret',
11
+ fullscreen: 'Fuld skærm',
12
+ loading: 'Indlæser…',
13
+ next: 'Næste',
14
+ noImage: 'Intet billede',
15
+ previous: 'Forrige',
16
+ rotate: 'Roter',
17
+ zoomIn: 'Zoom ind',
18
+ zoomOut: 'Zoom ud',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const de: MediaCopy = {
4
+ close: 'Schließen',
5
+ diagram: 'Diagramm',
6
+ download: 'Herunterladen',
7
+ failedToLoad: 'Laden fehlgeschlagen',
8
+ fitToView: 'An Fenster anpassen',
9
+ flipHorizontal: 'Horizontal spiegeln',
10
+ flipVertical: 'Vertikal spiegeln',
11
+ fullscreen: 'Vollbild',
12
+ loading: 'Wird geladen…',
13
+ next: 'Weiter',
14
+ noImage: 'Kein Bild',
15
+ previous: 'Zurück',
16
+ rotate: 'Drehen',
17
+ zoomIn: 'Vergrößern',
18
+ zoomOut: 'Verkleinern',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const en: MediaCopy = {
4
+ close: 'Close',
5
+ diagram: 'Diagram',
6
+ download: 'Download',
7
+ failedToLoad: 'Failed to load',
8
+ fitToView: 'Fit to view',
9
+ flipHorizontal: 'Flip horizontal',
10
+ flipVertical: 'Flip vertical',
11
+ fullscreen: 'Fullscreen',
12
+ loading: 'Loading…',
13
+ next: 'Next',
14
+ noImage: 'No image',
15
+ previous: 'Previous',
16
+ rotate: 'Rotate',
17
+ zoomIn: 'Zoom in',
18
+ zoomOut: 'Zoom out',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const es: MediaCopy = {
4
+ close: 'Cerrar',
5
+ diagram: 'Diagrama',
6
+ download: 'Descargar',
7
+ failedToLoad: 'Error al cargar',
8
+ fitToView: 'Ajustar a la ventana',
9
+ flipHorizontal: 'Voltear horizontalmente',
10
+ flipVertical: 'Voltear verticalmente',
11
+ fullscreen: 'Pantalla completa',
12
+ loading: 'Cargando…',
13
+ next: 'Siguiente',
14
+ noImage: 'Sin imagen',
15
+ previous: 'Anterior',
16
+ rotate: 'Girar',
17
+ zoomIn: 'Acercar',
18
+ zoomOut: 'Alejar',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const fr: MediaCopy = {
4
+ close: 'Fermer',
5
+ diagram: 'Diagramme',
6
+ download: 'Télécharger',
7
+ failedToLoad: 'Échec du chargement',
8
+ fitToView: 'Ajuster à la fenêtre',
9
+ flipHorizontal: 'Retourner horizontalement',
10
+ flipVertical: 'Retourner verticalement',
11
+ fullscreen: 'Plein écran',
12
+ loading: 'Chargement…',
13
+ next: 'Suivant',
14
+ noImage: 'Aucune image',
15
+ previous: 'Précédent',
16
+ rotate: 'Pivoter',
17
+ zoomIn: 'Zoom avant',
18
+ zoomOut: 'Zoom arrière',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const it: MediaCopy = {
4
+ close: 'Chiudi',
5
+ diagram: 'Diagramma',
6
+ download: 'Scarica',
7
+ failedToLoad: 'Caricamento non riuscito',
8
+ fitToView: 'Adatta alla finestra',
9
+ flipHorizontal: 'Capovolgi orizzontalmente',
10
+ flipVertical: 'Capovolgi verticalmente',
11
+ fullscreen: 'Schermo intero',
12
+ loading: 'Caricamento…',
13
+ next: 'Avanti',
14
+ noImage: 'Nessuna immagine',
15
+ previous: 'Indietro',
16
+ rotate: 'Ruota',
17
+ zoomIn: 'Ingrandisci',
18
+ zoomOut: 'Riduci',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const ja: MediaCopy = {
4
+ close: '閉じる',
5
+ diagram: 'ダイアグラム',
6
+ download: 'ダウンロード',
7
+ failedToLoad: '読み込めませんでした',
8
+ fitToView: '画面に合わせる',
9
+ flipHorizontal: '左右反転',
10
+ flipVertical: '上下反転',
11
+ fullscreen: '全画面表示',
12
+ loading: '読み込み中…',
13
+ next: '次へ',
14
+ noImage: '画像がありません',
15
+ previous: '前へ',
16
+ rotate: '回転',
17
+ zoomIn: '拡大',
18
+ zoomOut: '縮小',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const ko: MediaCopy = {
4
+ close: '닫기',
5
+ diagram: '다이어그램',
6
+ download: '다운로드',
7
+ failedToLoad: '불러오지 못했습니다',
8
+ fitToView: '화면에 맞추기',
9
+ flipHorizontal: '좌우 반전',
10
+ flipVertical: '상하 반전',
11
+ fullscreen: '전체 화면',
12
+ loading: '로딩 중…',
13
+ next: '다음',
14
+ noImage: '이미지 없음',
15
+ previous: '이전',
16
+ rotate: '회전',
17
+ zoomIn: '확대',
18
+ zoomOut: '축소',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const nl: MediaCopy = {
4
+ close: 'Sluiten',
5
+ diagram: 'Diagram',
6
+ download: 'Downloaden',
7
+ failedToLoad: 'Laden mislukt',
8
+ fitToView: 'Aanpassen aan venster',
9
+ flipHorizontal: 'Horizontaal spiegelen',
10
+ flipVertical: 'Verticaal spiegelen',
11
+ fullscreen: 'Volledig scherm',
12
+ loading: 'Laden…',
13
+ next: 'Volgende',
14
+ noImage: 'Geen afbeelding',
15
+ previous: 'Vorige',
16
+ rotate: 'Draaien',
17
+ zoomIn: 'Inzoomen',
18
+ zoomOut: 'Uitzoomen',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const no: MediaCopy = {
4
+ close: 'Lukk',
5
+ diagram: 'Diagram',
6
+ download: 'Last ned',
7
+ failedToLoad: 'Kunne ikke lastes',
8
+ fitToView: 'Tilpass til vinduet',
9
+ flipHorizontal: 'Speilvend horisontalt',
10
+ flipVertical: 'Speilvend vertikalt',
11
+ fullscreen: 'Fullskjerm',
12
+ loading: 'Laster…',
13
+ next: 'Neste',
14
+ noImage: 'Ingen bilde',
15
+ previous: 'Forrige',
16
+ rotate: 'Roter',
17
+ zoomIn: 'Zoom inn',
18
+ zoomOut: 'Zoom ut',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const pl: MediaCopy = {
4
+ close: 'Zamknij',
5
+ diagram: 'Diagram',
6
+ download: 'Pobierz',
7
+ failedToLoad: 'Nie udało się wczytać',
8
+ fitToView: 'Dopasuj do okna',
9
+ flipHorizontal: 'Odbij w poziomie',
10
+ flipVertical: 'Odbij w pionie',
11
+ fullscreen: 'Pełny ekran',
12
+ loading: 'Ładowanie…',
13
+ next: 'Dalej',
14
+ noImage: 'Brak obrazu',
15
+ previous: 'Wstecz',
16
+ rotate: 'Obróć',
17
+ zoomIn: 'Powiększ',
18
+ zoomOut: 'Pomniejsz',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const ptBR: MediaCopy = {
4
+ close: 'Fechar',
5
+ diagram: 'Diagrama',
6
+ download: 'Baixar',
7
+ failedToLoad: 'Falha ao carregar',
8
+ fitToView: 'Ajustar à janela',
9
+ flipHorizontal: 'Espelhar na horizontal',
10
+ flipVertical: 'Espelhar na vertical',
11
+ fullscreen: 'Tela cheia',
12
+ loading: 'Carregando…',
13
+ next: 'Próximo',
14
+ noImage: 'Sem imagem',
15
+ previous: 'Anterior',
16
+ rotate: 'Girar',
17
+ zoomIn: 'Aproximar',
18
+ zoomOut: 'Afastar',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const ru: MediaCopy = {
4
+ close: 'Закрыть',
5
+ diagram: 'Диаграмма',
6
+ download: 'Скачать',
7
+ failedToLoad: 'Не удалось загрузить',
8
+ fitToView: 'По размеру окна',
9
+ flipHorizontal: 'Отразить по горизонтали',
10
+ flipVertical: 'Отразить по вертикали',
11
+ fullscreen: 'Во весь экран',
12
+ loading: 'Загрузка…',
13
+ next: 'Далее',
14
+ noImage: 'Нет изображения',
15
+ previous: 'Назад',
16
+ rotate: 'Повернуть',
17
+ zoomIn: 'Увеличить',
18
+ zoomOut: 'Уменьшить',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const sv: MediaCopy = {
4
+ close: 'Stäng',
5
+ diagram: 'Diagram',
6
+ download: 'Ladda ner',
7
+ failedToLoad: 'Det gick inte att ladda',
8
+ fitToView: 'Anpassa till fönstret',
9
+ flipHorizontal: 'Vänd horisontellt',
10
+ flipVertical: 'Vänd vertikalt',
11
+ fullscreen: 'Helskärm',
12
+ loading: 'Laddar…',
13
+ next: 'Nästa',
14
+ noImage: 'Ingen bild',
15
+ previous: 'Föregående',
16
+ rotate: 'Rotera',
17
+ zoomIn: 'Zooma in',
18
+ zoomOut: 'Zooma ut',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const tr: MediaCopy = {
4
+ close: 'Kapat',
5
+ diagram: 'Diyagram',
6
+ download: 'İndir',
7
+ failedToLoad: 'Yüklenemedi',
8
+ fitToView: 'Pencereye sığdır',
9
+ flipHorizontal: 'Yatay çevir',
10
+ flipVertical: 'Dikey çevir',
11
+ fullscreen: 'Tam ekran',
12
+ loading: 'Yükleniyor…',
13
+ next: 'Sonraki',
14
+ noImage: 'Görsel yok',
15
+ previous: 'Önceki',
16
+ rotate: 'Döndür',
17
+ zoomIn: 'Yakınlaştır',
18
+ zoomOut: 'Uzaklaştır',
19
+ };
@@ -0,0 +1,19 @@
1
+ import type { MediaCopy } from '../types';
2
+
3
+ export const zh: MediaCopy = {
4
+ close: '关闭',
5
+ diagram: '图表',
6
+ download: '下载',
7
+ failedToLoad: '加载失败',
8
+ fitToView: '适应窗口',
9
+ flipHorizontal: '水平翻转',
10
+ flipVertical: '垂直翻转',
11
+ fullscreen: '全屏',
12
+ loading: '加载中…',
13
+ next: '下一个',
14
+ noImage: '没有图片',
15
+ previous: '上一个',
16
+ rotate: '旋转',
17
+ zoomIn: '放大',
18
+ zoomOut: '缩小',
19
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The media package's copy schema. English is the schema of record: every
3
+ * locale is typed against `MediaCopy`, so a missing key is a `tsc` error rather
4
+ * than `undefined` rendered into a toolbar button.
5
+ */
6
+ export interface MediaCopy {
7
+ close: string;
8
+ diagram: string;
9
+ download: string;
10
+ failedToLoad: string;
11
+ fitToView: string;
12
+ flipHorizontal: string;
13
+ flipVertical: string;
14
+ fullscreen: string;
15
+ loading: string;
16
+ next: string;
17
+ noImage: string;
18
+ previous: string;
19
+ rotate: string;
20
+ zoomIn: string;
21
+ zoomOut: string;
22
+ }
package/src/index.ts CHANGED
@@ -7,4 +7,4 @@
7
7
  // undo the split silently.
8
8
  export * from "./image-viewer";
9
9
  export type { MediaCopy } from "./i18n";
10
- export { DEFAULT_MEDIA_COPY } from "./i18n";
10
+ export { DEFAULT_MEDIA_COPY, MEDIA_DICTIONARIES, useMediaCopy } from "./i18n";