@gramps-connect/story 0.6.1 → 0.7.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.
@@ -37,4 +37,4 @@ export interface StoryEditorProps {
37
37
  }
38
38
  /** The default prepareImage. */
39
39
  export declare function prepareImage(file: Blob): Promise<NewImage>;
40
- export declare function StoryEditor({ initialStory, initialFiles, onChange, prepareImage: prepare, toolbar, brand, dark, strings: stringOverrides, playerStrings, parseDateText, placeSearch, mapStrings, }: StoryEditorProps): import("react").JSX.Element;
40
+ export declare function StoryEditor({ initialStory, initialFiles, onChange, prepareImage: prepare, toolbar, brand, dark, strings: stringOverrides, playerStrings, parseDateText, placeSearch, mapStrings: mapStringOverrides, }: StoryEditorProps): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  export { prepareImage, StoryEditor, type StoryEditorProps } from "./StoryEditor";
2
2
  export { MapEditor, MapEditorHelp, type MapEditorHandle, type MapEditorOverlay, type MapEditorProps, type MapEditorResult, type MapEditorStatus, } from "./MapEditor";
3
- export { MAP_EDITOR_ENGLISH, type MapEditorStrings } from "./mapEditorStrings";
3
+ export { MAP_EDITOR_ENGLISH, mapEditorStringsFor, type MapEditorStrings } from "./mapEditorStrings";
4
4
  export { PhotosField, type PhotosFieldProps } from "./PhotosField";
5
5
  export { PlaceField, type PlaceFieldProps, type PlaceUpdate } from "./PlaceField";
6
6
  export { PlaceMapDialog, type PlaceMapDialogProps } from "./PlaceMapDialog";
@@ -10,5 +10,5 @@ export { StoryTextField, type StoryTextFieldProps } from "./StoryTextField";
10
10
  export { SlideList, type SlideListProps } from "./SlideList";
11
11
  export { DateField, type DateFieldProps } from "./DateField";
12
12
  export { suggestEdtf, type DateTextParser } from "./dates";
13
- export { EDITOR_ENGLISH, type EditorStrings } from "./strings";
13
+ export { browserLanguages, EDITOR_ENGLISH, EDITOR_LANGUAGES, editorLanguage, editorStringsFor, type EditorStrings } from "./strings";
14
14
  export { addImage, addSlide, blankStory, COALESCE_MS, deleteSlide, duplicateSlide, findMedia, formatLatLon, moveSlide, nextSlideId, parseLatLon, patchPlace, pruneMedia, record, redo, roundDegrees, setSlidePhotos, slideDate, startHistory, storyFileName, storyPlaceChoices, undo, updateMedia, updateSlide, updateStory, usedMedia, type History, type NewImage, } from "./model";
@@ -48,3 +48,6 @@ export interface MapEditorStrings {
48
48
  helpImageRemove: string;
49
49
  }
50
50
  export declare const MAP_EDITOR_ENGLISH: MapEditorStrings;
51
+ /** The map editor's words in the first of `languages` it has; English
52
+ * when none. */
53
+ export declare function mapEditorStringsFor(...languages: (string | undefined)[]): MapEditorStrings;
@@ -5,6 +5,7 @@ export interface EditorStrings {
5
5
  language: string;
6
6
  languageHint: string;
7
7
  map: string;
8
+ mapHint: string;
8
9
  mapStandard: string;
9
10
  mapHistorical: string;
10
11
  mapAuto: string;
@@ -46,6 +47,8 @@ export interface EditorStrings {
46
47
  placeKeeps: (what: string) => string;
47
48
  editMap: string;
48
49
  editMapHint: string;
50
+ /** The map editor's help on adding an image, where images are files. */
51
+ mapHelpImage: string;
49
52
  mapDialogTitle: (placeName: string | undefined) => string;
50
53
  mapHelp: string;
51
54
  importShapes: string;
@@ -78,3 +81,17 @@ export interface EditorStrings {
78
81
  openingSlide: string;
79
82
  }
80
83
  export declare const EDITOR_ENGLISH: EditorStrings;
84
+ /** The browser's languages, most preferred first (none outside one). */
85
+ export declare function browserLanguages(): readonly string[];
86
+ /** The first of `languages` (BCP 47 tags, or Gramps' de_AT form) the
87
+ * editors have words for, as its base code: "de-CH" is "de". English
88
+ * counts, so "en-US, de" stays English. */
89
+ export declare function editorLanguage(languages: readonly (string | undefined)[]): string | undefined;
90
+ /** The translations for the first of `languages` the editors have;
91
+ * none for English. */
92
+ export declare function translationFor(languages: readonly (string | undefined)[]): import("./translations").EditorTranslation | undefined;
93
+ /** The editor's words in the first of `languages` it has; English when
94
+ * none. */
95
+ export declare function editorStringsFor(...languages: (string | undefined)[]): EditorStrings;
96
+ /** Languages the editors have words for, besides English. */
97
+ export declare const EDITOR_LANGUAGES: string[];
@@ -0,0 +1,4 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+ export declare const editor: EditorStrings;
4
+ export declare const mapEditor: MapEditorStrings;
@@ -0,0 +1,4 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+ export declare const editor: EditorStrings;
4
+ export declare const mapEditor: MapEditorStrings;
@@ -0,0 +1,4 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+ export declare const editor: EditorStrings;
4
+ export declare const mapEditor: MapEditorStrings;
@@ -0,0 +1,7 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+ export interface EditorTranslation {
4
+ editor: EditorStrings;
5
+ mapEditor: MapEditorStrings;
6
+ }
7
+ export declare const TRANSLATIONS: Record<string, EditorTranslation>;
@@ -0,0 +1,4 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+ export declare const editor: EditorStrings;
4
+ export declare const mapEditor: MapEditorStrings;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramps-connect/story",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Read, write, validate and present .story.zip files: portable, versioned family-history stories.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -35,7 +35,8 @@ import {
35
35
  } from "../mapEdit";
36
36
  import { mapStyleUrl } from "../player/mapStyles";
37
37
  import { defaultColors } from "../player/theme";
38
- import { MAP_EDITOR_ENGLISH, type MapEditorStrings } from "./mapEditorStrings";
38
+ import { mapEditorStringsFor, type MapEditorStrings } from "./mapEditorStrings";
39
+ import { browserLanguages } from "./strings";
39
40
 
40
41
  type DrawMode = "point" | "linestring" | "polygon" | "rectangle" | "select";
41
42
 
@@ -316,7 +317,7 @@ export const MapEditor = forwardRef<MapEditorHandle, MapEditorProps>(function Ma
316
317
  initial, initialView, resolveImage, releaseImage, onAddImage, onStatus, onError, dark = false, defaultColor, labelColors,
317
318
  toolbarTop = 12, overlayNames = true, renderOverlayOptions, strings: stringOverrides,
318
319
  }, ref) {
319
- const strings = { ...MAP_EDITOR_ENGLISH, ...stringOverrides };
320
+ const strings = { ...mapEditorStringsFor(...browserLanguages()), ...stringOverrides };
320
321
  // Callbacks and words the once-per-map listeners below read, kept live.
321
322
  const live = useRef({ strings, resolveImage, releaseImage, onError });
322
323
  live.current = { strings, resolveImage, releaseImage, onError };
@@ -1510,7 +1511,7 @@ export const MapEditor = forwardRef<MapEditorHandle, MapEditorProps>(function Ma
1510
1511
  /** How to use the map editor, for the host to show where it likes (Gramps
1511
1512
  * Connect: an info button's modal). */
1512
1513
  export function MapEditorHelp({ strings: stringOverrides, images = true }: { strings?: Partial<MapEditorStrings>; images?: boolean }) {
1513
- const strings = { ...MAP_EDITOR_ENGLISH, ...stringOverrides };
1514
+ const strings = { ...mapEditorStringsFor(...browserLanguages()), ...stringOverrides };
1514
1515
  return (
1515
1516
  <Stack gap="md">
1516
1517
  <Text size="sm" fw={600}>{strings.helpDrawing}</Text>
@@ -18,12 +18,6 @@ import type { EditorStrings } from "./strings";
18
18
 
19
19
  const BAR_HEIGHT = 56;
20
20
 
21
- /** Where the map editor's English is Gramps Connect's (images come from
22
- * its media there), the story editor's own. */
23
- const STORY_MAP_WORDS: Partial<MapEditorStrings> = {
24
- helpImage: "Image: add a picture file, such as a scan of an old map -- see",
25
- };
26
-
27
21
  export interface PlaceMapDialogProps {
28
22
  place: Place | undefined;
29
23
  doc: StoryDoc;
@@ -83,7 +77,9 @@ function OverlayDateField({ value, language, parse, strings, onChange }: {
83
77
  export function PlaceMapDialog({
84
78
  place, doc, files, dark, parseDateText, strings, mapStrings: mapStringOverrides, onCancel, onDone,
85
79
  }: PlaceMapDialogProps) {
86
- const mapStrings = { ...STORY_MAP_WORDS, ...mapStringOverrides };
80
+ // The map editor's help on images is Gramps Connect's (images come from
81
+ // its media there); here they're files.
82
+ const mapStrings = { helpImage: strings.mapHelpImage, ...mapStringOverrides };
87
83
  const editorRef = useRef<MapEditorHandle>(null);
88
84
  const initial = useMemo(() => placeToEditor(place), [place]);
89
85
  // The story's media and files with what's been added here.
@@ -30,7 +30,8 @@ import type { MapEditorStrings } from "./mapEditorStrings";
30
30
  import { WIKIDATA_PLACE_SEARCH, type PlaceSearch } from "./placeSearch";
31
31
  import { SlideList } from "./SlideList";
32
32
  import { StoryTextField } from "./StoryTextField";
33
- import { EDITOR_ENGLISH, type EditorStrings } from "./strings";
33
+ import { browserLanguages, editorStringsFor, type EditorStrings } from "./strings";
34
+ import { mapEditorStringsFor } from "./mapEditorStrings";
34
35
 
35
36
  const BAR_HEIGHT = 56;
36
37
  /** The story's own settings, in place of a slide index. */
@@ -102,9 +103,14 @@ function Thumb({ doc, slide, resolveMedia }: { doc: StoryDoc; slide: Slide; reso
102
103
 
103
104
  export function StoryEditor({
104
105
  initialStory, initialFiles, onChange, prepareImage: prepare = prepareImage, toolbar, brand, dark = false,
105
- strings: stringOverrides, playerStrings, parseDateText = suggestEdtf, placeSearch = WIKIDATA_PLACE_SEARCH, mapStrings,
106
+ strings: stringOverrides, playerStrings, parseDateText = suggestEdtf, placeSearch = WIKIDATA_PLACE_SEARCH, mapStrings: mapStringOverrides,
106
107
  }: StoryEditorProps) {
107
- const strings = useMemo(() => ({ ...EDITOR_ENGLISH, ...stringOverrides }), [stringOverrides]);
108
+ // In the browser's language where there's a translation, over English.
109
+ const strings = useMemo(() => ({ ...editorStringsFor(...browserLanguages()), ...stringOverrides }), [stringOverrides]);
110
+ const mapStrings = useMemo(
111
+ () => ({ ...mapEditorStringsFor(...browserLanguages()), helpImage: strings.mapHelpImage, ...mapStringOverrides }),
112
+ [strings, mapStringOverrides],
113
+ );
108
114
  const [history, setHistory] = useState<History>(() => startHistory(initialStory));
109
115
  const [selected, setSelected] = useState(0);
110
116
  const [presenting, setPresenting] = useState(false);
@@ -271,7 +277,7 @@ export function StoryEditor({
271
277
  onChange={(e) => apply(updateStory(doc, { language: e.currentTarget.value.trim() || undefined }), "story:language")}
272
278
  />
273
279
  <Select
274
- label={strings.map} w={320} allowDeselect={false}
280
+ label={strings.map} description={strings.mapHint} w={320} allowDeselect={false}
275
281
  data={[
276
282
  { value: "auto", label: strings.mapAuto },
277
283
  { value: "standard", label: strings.mapStandard },
@@ -2,7 +2,7 @@ export { prepareImage, StoryEditor, type StoryEditorProps } from "./StoryEditor"
2
2
  export {
3
3
  MapEditor, MapEditorHelp, type MapEditorHandle, type MapEditorOverlay, type MapEditorProps, type MapEditorResult, type MapEditorStatus,
4
4
  } from "./MapEditor";
5
- export { MAP_EDITOR_ENGLISH, type MapEditorStrings } from "./mapEditorStrings";
5
+ export { MAP_EDITOR_ENGLISH, mapEditorStringsFor, type MapEditorStrings } from "./mapEditorStrings";
6
6
  export { PhotosField, type PhotosFieldProps } from "./PhotosField";
7
7
  export { PlaceField, type PlaceFieldProps, type PlaceUpdate } from "./PlaceField";
8
8
  export { PlaceMapDialog, type PlaceMapDialogProps } from "./PlaceMapDialog";
@@ -12,7 +12,7 @@ export { StoryTextField, type StoryTextFieldProps } from "./StoryTextField";
12
12
  export { SlideList, type SlideListProps } from "./SlideList";
13
13
  export { DateField, type DateFieldProps } from "./DateField";
14
14
  export { suggestEdtf, type DateTextParser } from "./dates";
15
- export { EDITOR_ENGLISH, type EditorStrings } from "./strings";
15
+ export { browserLanguages, EDITOR_ENGLISH, EDITOR_LANGUAGES, editorLanguage, editorStringsFor, type EditorStrings } from "./strings";
16
16
  export {
17
17
  addImage, addSlide, blankStory, COALESCE_MS, deleteSlide, duplicateSlide, findMedia, formatLatLon, moveSlide, nextSlideId, parseLatLon,
18
18
  patchPlace, pruneMedia, record, redo, roundDegrees, setSlidePhotos, slideDate, startHistory, storyFileName, storyPlaceChoices, undo,
@@ -1,5 +1,7 @@
1
- // The map editor's words. English here; Gramps Connect passes its own
2
- // translations (its t() is keyed by these same English texts).
1
+ // The map editor's words, in English and the story editor's other
2
+ // languages (translations/). Gramps Connect passes its own where it has
3
+ // them (its t() is keyed by these same English texts).
4
+ import { translationFor } from "./strings";
3
5
 
4
6
  export interface MapEditorStrings {
5
7
  point: string;
@@ -107,3 +109,9 @@ export const MAP_EDITOR_ENGLISH: MapEditorStrings = {
107
109
  + "working on the image's exact current shape either way.",
108
110
  helpImageRemove: "Click its × handle to remove it.",
109
111
  };
112
+
113
+ /** The map editor's words in the first of `languages` it has; English
114
+ * when none. */
115
+ export function mapEditorStringsFor(...languages: (string | undefined)[]): MapEditorStrings {
116
+ return translationFor(languages)?.mapEditor ?? MAP_EDITOR_ENGLISH;
117
+ }
@@ -1,5 +1,8 @@
1
- // The editor's own words. English here; a host with its own translations
2
- // (Gramps Connect) passes StoryEditor's `strings` instead.
1
+ // The editor's own words, in English and the player's other languages
2
+ // (translations/). The editor picks by the browser's languages; a host
3
+ // with its own translations (Gramps Connect) passes StoryEditor's
4
+ // `strings` instead.
5
+ import { TRANSLATIONS } from "./translations";
3
6
 
4
7
  export interface EditorStrings {
5
8
  story: string;
@@ -8,6 +11,7 @@ export interface EditorStrings {
8
11
  language: string;
9
12
  languageHint: string;
10
13
  map: string;
14
+ mapHint: string;
11
15
  mapStandard: string;
12
16
  mapHistorical: string;
13
17
  mapAuto: string;
@@ -49,6 +53,8 @@ export interface EditorStrings {
49
53
  placeKeeps: (what: string) => string;
50
54
  editMap: string;
51
55
  editMapHint: string;
56
+ /** The map editor's help on adding an image, where images are files. */
57
+ mapHelpImage: string;
52
58
  mapDialogTitle: (placeName: string | undefined) => string;
53
59
  mapHelp: string;
54
60
  importShapes: string;
@@ -88,6 +94,7 @@ export const EDITOR_ENGLISH: EditorStrings = {
88
94
  language: "Language",
89
95
  languageHint: "The language the story is written in, e.g. en, de or fr-CA. Dates are read in it.",
90
96
  map: "Map",
97
+ mapHint: "How the map starts when the story plays. Readers can still change it.",
91
98
  mapStandard: "Today's map",
92
99
  mapHistorical: "Historical maps for each slide's year",
93
100
  mapAuto: "Viewer's choice",
@@ -129,6 +136,7 @@ export const EDITOR_ENGLISH: EditorStrings = {
129
136
  placeKeeps: (what) => `This place also has ${what}.`,
130
137
  editMap: "Edit map",
131
138
  editMapHint: "Shapes under the pin (a parish, a route) and scans of old maps laid over it.",
139
+ mapHelpImage: "Image: add a picture file, such as a scan of an old map -- see",
132
140
  mapDialogTitle: (placeName) => (placeName ? `Map: ${placeName}` : "Map"),
133
141
  mapHelp: "How to use this editor",
134
142
  importShapes: "Import shapes",
@@ -160,3 +168,36 @@ export const EDITOR_ENGLISH: EditorStrings = {
160
168
  problems: (count) => (count === 1 ? "1 problem to fix before saving" : `${count} problems to fix before saving`),
161
169
  openingSlide: "Opening slide",
162
170
  };
171
+
172
+ /** The browser's languages, most preferred first (none outside one). */
173
+ export function browserLanguages(): readonly string[] {
174
+ if (typeof navigator === "undefined") return [];
175
+ return navigator.languages?.length ? navigator.languages : [navigator.language];
176
+ }
177
+
178
+ /** The first of `languages` (BCP 47 tags, or Gramps' de_AT form) the
179
+ * editors have words for, as its base code: "de-CH" is "de". English
180
+ * counts, so "en-US, de" stays English. */
181
+ export function editorLanguage(languages: readonly (string | undefined)[]): string | undefined {
182
+ for (const tag of languages) {
183
+ const base = tag?.toLowerCase().split(/[-_]/)[0];
184
+ if (base === "en" || (base && Object.prototype.hasOwnProperty.call(TRANSLATIONS, base))) return base;
185
+ }
186
+ return undefined;
187
+ }
188
+
189
+ /** The translations for the first of `languages` the editors have;
190
+ * none for English. */
191
+ export function translationFor(languages: readonly (string | undefined)[]) {
192
+ const lang = editorLanguage(languages);
193
+ return lang && lang !== "en" ? TRANSLATIONS[lang] : undefined;
194
+ }
195
+
196
+ /** The editor's words in the first of `languages` it has; English when
197
+ * none. */
198
+ export function editorStringsFor(...languages: (string | undefined)[]): EditorStrings {
199
+ return translationFor(languages)?.editor ?? EDITOR_ENGLISH;
200
+ }
201
+
202
+ /** Languages the editors have words for, besides English. */
203
+ export const EDITOR_LANGUAGES = Object.keys(TRANSLATIONS);
@@ -0,0 +1,140 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+
4
+ export const editor: EditorStrings = {
5
+ story: "Geschichte",
6
+ storySettings: "Titel, Sprache und Karte",
7
+ title: "Titel",
8
+ language: "Sprache",
9
+ languageHint: "Die Sprache, in der die Geschichte geschrieben ist, z. B. en, de oder fr-CA. Daten werden in ihr gelesen.",
10
+ map: "Karte",
11
+ mapHint: "Wie die Karte beim Abspielen der Geschichte beginnt. Leser können sie trotzdem ändern.",
12
+ mapStandard: "Heutige Karte",
13
+ mapHistorical: "Historische Karten zum Jahr jeder Folie",
14
+ mapAuto: "Wahl des Betrachters",
15
+ mapNone: "Keine Karte",
16
+ slides: "Folien",
17
+ untitledSlide: "Folie ohne Titel",
18
+ addSlide: "Folie hinzufügen",
19
+ duplicateSlide: "Duplizieren",
20
+ deleteSlide: "Löschen",
21
+ moveUp: "Nach oben",
22
+ moveDown: "Nach unten",
23
+ slideTitle: "Titel",
24
+ text: "Text",
25
+ bold: "Fett",
26
+ italic: "Kursiv",
27
+ date: "Datum",
28
+ dateHint: "So, wie Leser es sehen sollen, z. B. um 1850, 3. Mai 1850, zwischen 1850 und 1860.",
29
+ preciseDate: "Genaues Datum (EDTF)",
30
+ preciseDateHint: "Setzt die Folie auf die Zeitleiste und wählt die historischen Karten.",
31
+ preciseDateFollows: "Aus dem geschriebenen Datum ermittelt.",
32
+ preciseDateNotUnderstood: "Kein Datum, das die Zeitleiste verwenden kann.",
33
+ matchDateAsWritten: "Aus dem Datum ermitteln",
34
+ noPreciseDate: "Keines: Diese Folie bleibt außerhalb der Zeitleiste.",
35
+ place: "Ort",
36
+ placeName: "Name",
37
+ removePlace: "Ort entfernen",
38
+ findPlace: "Auf Wikidata suchen",
39
+ findPlaceHint: "Sucht den Namen auf Wikidata, um die Stecknadel dorthin zu setzen. Nur der Name wird gesendet, und nur, wenn Sie es wünschen.",
40
+ findPlaceNone: "Wikidata kennt keinen Ort mit diesem Namen.",
41
+ findPlaceFailed: "Wikidata war nicht erreichbar.",
42
+ findPlaceNoPoint: (name) => `Wikidata hat keine Kartenposition für ${name}.`,
43
+ coordinates: "Breite, Länge",
44
+ coordinatesHint: "z. B. 47.3769, 8.5417",
45
+ coordinatesInvalid: "Zwei Zahlen: Breite (-90 bis 90), dann Länge (-180 bis 180).",
46
+ placeMap: "Karte: klicken, um die Stecknadel zu setzen",
47
+ placeMapHint: "Klicken Sie auf die Karte, um die Stecknadel dorthin zu setzen, oder ziehen Sie die Stecknadel.",
48
+ placeShape: "eine Form",
49
+ placeOverlays: (count) => (count === 1 ? "eine Kartenüberlagerung" : `${count} Kartenüberlagerungen`),
50
+ placeKeeps: (what) => `Dieser Ort hat außerdem ${what}.`,
51
+ editMap: "Karte bearbeiten",
52
+ editMapHint: "Formen unter der Stecknadel (eine Pfarrei, eine Route) und darübergelegte Scans alter Karten.",
53
+ mapHelpImage: "Bild: Fügen Sie eine Bilddatei hinzu, etwa den Scan einer alten Karte – siehe",
54
+ mapDialogTitle: (placeName) => (placeName ? `Karte: ${placeName}` : "Karte"),
55
+ mapHelp: "So benutzen Sie diesen Editor",
56
+ importShapes: "Formen importieren",
57
+ importShapesHint: "Fügt die Punkte, Linien und Flächen einer KML-, GPX- oder GeoJSON-Datei zu dieser Karte hinzu.",
58
+ importFailed: (fileName) => `${fileName} konnte nicht als KML-, GPX- oder GeoJSON-Datei gelesen werden.`,
59
+ importNothing: (fileName) => `${fileName} enthält keine Formen.`,
60
+ overlayAlready: "Dieses Bild ist bereits auf dieser Karte.",
61
+ overlayDate: "Jahre, die sie zeigt",
62
+ overlayDateHint: "z. B. 1850, oder 1850 bis 1880. Mit historischen Karten ist sie auf Folien außerhalb dieser Jahre ausgeblendet.",
63
+ cancel: "Abbrechen",
64
+ done: "Fertig",
65
+ photos: "Fotos",
66
+ photosHint: "Fügen Sie ein Foto hinzu, ziehen Sie es hierher oder fügen Sie es ein. Es wird für den Bildschirm verkleinert, und versteckte Angaben wie Kamera und Standort werden weggelassen.",
67
+ addPhoto: "Foto hinzufügen",
68
+ replacePhoto: "Ersetzen",
69
+ movePhotoUp: "Foto nach oben",
70
+ movePhotoDown: "Foto nach unten",
71
+ removePhoto: "Entfernen",
72
+ caption: "Bildunterschrift",
73
+ credit: "Bildnachweis",
74
+ creditHint: "Wer es aufgenommen hat oder woher es stammt",
75
+ description: "Beschreibung",
76
+ descriptionHint: "Was das Foto zeigt, für alle, die es nicht sehen können.",
77
+ photoFailed: (name) => `${name} konnte nicht verwendet werden: Dieser Browser kann es nicht als Bild lesen.`,
78
+ undo: "Rückgängig",
79
+ redo: "Wiederholen",
80
+ present: "Vorführen",
81
+ preview: "Vorschau",
82
+ problems: (count) => (count === 1 ? "1 Problem vor dem Speichern beheben" : `${count} Probleme vor dem Speichern beheben`),
83
+ openingSlide: "Eröffnungsfolie",
84
+ };
85
+
86
+ export const mapEditor: MapEditorStrings = {
87
+ point: "Punkt",
88
+ line: "Linie",
89
+ polygon: "Polygon",
90
+ rectangle: "Rechteck",
91
+ select: "Auswählen",
92
+ image: "Bild",
93
+ editOptions: "Bearbeitungsoptionen",
94
+ labelText: "Beschriftung",
95
+ regionName: "Name des Gebiets (optional)",
96
+ fillOpacity: "Deckkraft der Füllung",
97
+ scale: "Skalieren",
98
+ transform: "Verzerren",
99
+ overlayName: "Name der Überlagerung (optional)",
100
+ opacity: "Deckkraft",
101
+ stackingOrder: "Stapelreihenfolge",
102
+ removeImage: "Dieses Bild entfernen",
103
+ resizeImage: "Ziehen, um die Größe zu ändern (behält die genaue Form, verzerrt oder nicht)",
104
+ rotateImage: "Ziehen, um zu drehen",
105
+ warpCorner: "Ziehen, um diese Ecke zu verzerren",
106
+ basemapFailed: "Die Grundkarte konnte nicht geladen werden.",
107
+ imageFailed: "Das Bild konnte nicht geladen werden.",
108
+ lockedOne: "1 Form kann hier nicht bearbeitet werden",
109
+ lockedMany: (count) => `${count} Formen können hier nicht bearbeitet werden`,
110
+ lockedExplain: "Zum Beispiel ein Gebiet mit einem Loch oder eines, dessen Umriss sich selbst kreuzt. Es wird gestrichelt gezeichnet und genau so gespeichert, wie es war.",
111
+ helpTitle: "So benutzen Sie diesen Editor",
112
+ helpDrawing: "Zeichnen",
113
+ helpPoint: "Punkt: einmal klicken, um ihn zu setzen.",
114
+ helpLine: "Linie, Polygon: klicken, um jeden Punkt zu setzen, dann erneut auf die Markierung des ersten Punktes klicken, "
115
+ + "um abzuschließen – sie sieht anders aus, sobald es genug Punkte gibt, um die Form zu schließen. Auch",
116
+ helpEnterFinishes: "schließt sie ab;",
117
+ helpEscCancels: "bricht sie ab.",
118
+ helpRectangle: "Rechteck: von einer Ecke zur gegenüberliegenden Ecke klicken und ziehen.",
119
+ helpImage: "Bild: ein Medienobjekt zum Überlagern wählen – siehe",
120
+ helpImages: "Bilder",
121
+ helpBelow: "unten.",
122
+ helpEditing: "Eine Form bearbeiten",
123
+ helpSelect: "Auswählen: auf eine Form klicken, um sie auszuwählen, dann sie oder einen ihrer Griffe ziehen.",
124
+ helpVertex: "Einen Eckpunkt ziehen, um ihn zu verschieben. Für einen neuen Punkt auf einer Linie, einem Polygon oder Rechteck "
125
+ + "die kleine Mittelpunktmarkierung zwischen zwei Eckpunkten nach außen ziehen.",
126
+ helpColor: "Das Farbfeld legt die Farbe der nächsten Form fest – oder ändert, wenn eine Form ausgewählt ist, deren Farbe.",
127
+ helpDelete: "Eine ausgewählte Form oder ein Bild löschen mit",
128
+ helpOr: "oder",
129
+ helpRotate: "Ein ausgewähltes Polygon, Rechteck oder eine Linie drehen:",
130
+ helpRotateHandle: "gedrückt halten und dabei einen ihrer Griffe ziehen.",
131
+ helpImageMove: "Irgendwo auf ein Bild klicken und ziehen, um es zu verschieben.",
132
+ helpImageResize: "Seinen ↘-Griff ziehen, um die Größe zu ändern – es wächst oder schrumpft um seine eigene Mitte "
133
+ + "und behält seine genaue Form (auch bei einem verzerrten Bild).",
134
+ helpImageRotate: "Seinen ↻-Griff ziehen, um es zu drehen – praktisch, um eine gescannte alte Karte auszurichten.",
135
+ helpImageTransform: "Auf „Verzerren“ umschalten, um zusätzlich 4 Eckgriffe zu zeigen, die sich einzeln ziehen lassen "
136
+ + "(Entzerrung) – für eine gescannte Karte ohne einheitlichen Maßstab oder einheitliche Ausrichtung. "
137
+ + "Zurück auf „Skalieren“ blendet diese 4 Griffe nur wieder aus; Verschieben, Größe ändern und Drehen "
138
+ + "wirken in beiden Fällen auf die genaue aktuelle Form des Bildes.",
139
+ helpImageRemove: "Auf seinen ×-Griff klicken, um es zu entfernen.",
140
+ };
@@ -0,0 +1,140 @@
1
+ import type { EditorStrings } from "../strings";
2
+ import type { MapEditorStrings } from "../mapEditorStrings";
3
+
4
+ export const editor: EditorStrings = {
5
+ story: "Historia",
6
+ storySettings: "Título, idioma y mapa",
7
+ title: "Título",
8
+ language: "Idioma",
9
+ languageHint: "El idioma en que está escrita la historia, p. ej. en, de o fr-CA. Las fechas se leen en él.",
10
+ map: "Mapa",
11
+ mapHint: "Cómo empieza el mapa al reproducir la historia. Los lectores aún pueden cambiarlo.",
12
+ mapStandard: "Mapa actual",
13
+ mapHistorical: "Mapas históricos del año de cada diapositiva",
14
+ mapAuto: "A elección del lector",
15
+ mapNone: "Sin mapa",
16
+ slides: "Diapositivas",
17
+ untitledSlide: "Diapositiva sin título",
18
+ addSlide: "Añadir diapositiva",
19
+ duplicateSlide: "Duplicar",
20
+ deleteSlide: "Eliminar",
21
+ moveUp: "Subir",
22
+ moveDown: "Bajar",
23
+ slideTitle: "Título",
24
+ text: "Texto",
25
+ bold: "Negrita",
26
+ italic: "Cursiva",
27
+ date: "Fecha",
28
+ dateHint: "Como deben verla los lectores, p. ej. hacia 1850, 3 de mayo de 1850, entre 1850 y 1860.",
29
+ preciseDate: "Fecha precisa (EDTF)",
30
+ preciseDateHint: "Sitúa la diapositiva en la línea de tiempo y elige los mapas históricos.",
31
+ preciseDateFollows: "Deducida de la fecha tal como está escrita.",
32
+ preciseDateNotUnderstood: "No es una fecha que la línea de tiempo pueda usar.",
33
+ matchDateAsWritten: "Deducirla de la fecha",
34
+ noPreciseDate: "Ninguna: esta diapositiva queda fuera de la línea de tiempo.",
35
+ place: "Lugar",
36
+ placeName: "Nombre",
37
+ removePlace: "Quitar el lugar",
38
+ findPlace: "Buscar en Wikidata",
39
+ findPlaceHint: "Busca el nombre en Wikidata para poner allí el marcador. Solo se envía el nombre, y solo cuando usted lo pide.",
40
+ findPlaceNone: "Wikidata no tiene ningún lugar con ese nombre.",
41
+ findPlaceFailed: "No se pudo conectar con Wikidata.",
42
+ findPlaceNoPoint: (name) => `Wikidata no tiene una posición en el mapa para ${name}.`,
43
+ coordinates: "Latitud, longitud",
44
+ coordinatesHint: "p. ej. 47.3769, 8.5417",
45
+ coordinatesInvalid: "Dos números: la latitud (de -90 a 90) y luego la longitud (de -180 a 180).",
46
+ placeMap: "Mapa: haga clic para poner el marcador",
47
+ placeMapHint: "Haga clic en el mapa para poner allí el marcador, o arrastre el marcador.",
48
+ placeShape: "una forma",
49
+ placeOverlays: (count) => (count === 1 ? "una superposición de mapa" : `${count} superposiciones de mapa`),
50
+ placeKeeps: (what) => `Este lugar también tiene ${what}.`,
51
+ editMap: "Editar el mapa",
52
+ editMapHint: "Formas bajo el marcador (una parroquia, una ruta) y escaneos de mapas antiguos superpuestos.",
53
+ mapHelpImage: "Imagen: añada un archivo de imagen, como el escaneo de un mapa antiguo; consulte",
54
+ mapDialogTitle: (placeName) => (placeName ? `Mapa: ${placeName}` : "Mapa"),
55
+ mapHelp: "Cómo usar este editor",
56
+ importShapes: "Importar formas",
57
+ importShapesHint: "Añade a este mapa los puntos, líneas y áreas de un archivo KML, GPX o GeoJSON.",
58
+ importFailed: (fileName) => `No se pudo leer ${fileName} como archivo KML, GPX o GeoJSON.`,
59
+ importNothing: (fileName) => `${fileName} no contiene formas.`,
60
+ overlayAlready: "Esa imagen ya está en este mapa.",
61
+ overlayDate: "Años que muestra",
62
+ overlayDateHint: "p. ej. 1850, o de 1850 a 1880. Con mapas históricos, se oculta en las diapositivas fuera de esos años.",
63
+ cancel: "Cancelar",
64
+ done: "Listo",
65
+ photos: "Fotos",
66
+ photosHint: "Añada, suelte o pegue una foto. Se reduce para la pantalla y se omiten datos ocultos como la cámara y la ubicación.",
67
+ addPhoto: "Añadir foto",
68
+ replacePhoto: "Reemplazar",
69
+ movePhotoUp: "Subir la foto",
70
+ movePhotoDown: "Bajar la foto",
71
+ removePhoto: "Quitar",
72
+ caption: "Pie de foto",
73
+ credit: "Créditos",
74
+ creditHint: "Quién la tomó o de dónde procede",
75
+ description: "Descripción",
76
+ descriptionHint: "Lo que muestra la foto, para quien no pueda verla.",
77
+ photoFailed: (name) => `No se pudo usar ${name}: este navegador no puede leerlo como imagen.`,
78
+ undo: "Deshacer",
79
+ redo: "Rehacer",
80
+ present: "Presentar",
81
+ preview: "Vista previa",
82
+ problems: (count) => (count === 1 ? "1 problema que corregir antes de guardar" : `${count} problemas que corregir antes de guardar`),
83
+ openingSlide: "Diapositiva inicial",
84
+ };
85
+
86
+ export const mapEditor: MapEditorStrings = {
87
+ point: "Punto",
88
+ line: "Línea",
89
+ polygon: "Polígono",
90
+ rectangle: "Rectángulo",
91
+ select: "Seleccionar",
92
+ image: "Imagen",
93
+ editOptions: "Opciones de edición",
94
+ labelText: "Texto de la etiqueta",
95
+ regionName: "Nombre de la región (opcional)",
96
+ fillOpacity: "Opacidad del relleno",
97
+ scale: "Escalar",
98
+ transform: "Deformar",
99
+ overlayName: "Nombre de la superposición (opcional)",
100
+ opacity: "Opacidad",
101
+ stackingOrder: "Orden de apilamiento",
102
+ removeImage: "Quitar esta imagen",
103
+ resizeImage: "Arrastre para cambiar el tamaño (mantiene su forma exacta, deformada o no)",
104
+ rotateImage: "Arrastre para girar",
105
+ warpCorner: "Arrastre para deformar esta esquina",
106
+ basemapFailed: "No se pudo cargar el mapa base.",
107
+ imageFailed: "No se pudo cargar la imagen.",
108
+ lockedOne: "1 forma no se puede editar aquí",
109
+ lockedMany: (count) => `${count} formas no se pueden editar aquí`,
110
+ lockedExplain: "Por ejemplo, una región con un hueco, o una cuyo contorno se cruza a sí mismo. Se dibuja con trazo discontinuo y se guarda exactamente como estaba.",
111
+ helpTitle: "Cómo usar este editor",
112
+ helpDrawing: "Dibujar",
113
+ helpPoint: "Punto: haga clic una vez para colocarlo.",
114
+ helpLine: "Línea, Polígono: haga clic para colocar cada punto y luego vuelva a hacer clic en el marcador del primer punto "
115
+ + "para terminar; cambia de aspecto cuando hay puntos suficientes para cerrar la forma. Pulsar",
116
+ helpEnterFinishes: "también la termina;",
117
+ helpEscCancels: "la cancela.",
118
+ helpRectangle: "Rectángulo: haga clic y arrastre de una esquina a la opuesta.",
119
+ helpImage: "Imagen: elija un objeto multimedia para superponer; consulte",
120
+ helpImages: "Imágenes",
121
+ helpBelow: "más abajo.",
122
+ helpEditing: "Editar una forma",
123
+ helpSelect: "Seleccionar: haga clic en una forma para seleccionarla y luego arrástrela a ella o a uno de sus tiradores.",
124
+ helpVertex: "Arrastre un vértice para moverlo. Para añadir un punto nuevo a una Línea, Polígono o Rectángulo, arrastre hacia fuera "
125
+ + "el pequeño marcador del punto medio entre dos vértices.",
126
+ helpColor: "La muestra de color fija el color de la siguiente forma o, con una forma seleccionada, cambia su color.",
127
+ helpDelete: "Elimine una forma o imagen seleccionada con",
128
+ helpOr: "o",
129
+ helpRotate: "Para girar un Polígono, Rectángulo o Línea seleccionado, mantenga pulsada",
130
+ helpRotateHandle: "mientras arrastra uno de sus tiradores.",
131
+ helpImageMove: "Arrastre desde cualquier punto de una imagen para moverla.",
132
+ helpImageResize: "Arrastre su tirador ↘ para cambiar el tamaño: crece o se encoge desde su propio centro "
133
+ + "y mantiene su forma exacta (también en una imagen deformada).",
134
+ helpImageRotate: "Arrastre su tirador ↻ para girarla: útil para alinear un mapa antiguo escaneado.",
135
+ helpImageTransform: "Cambie a «Deformar» para mostrar además 4 tiradores de esquina que se arrastran por separado "
136
+ + "(georreferenciación elástica): para un mapa escaneado que no tiene una escala u orientación uniforme. "
137
+ + "Volver a «Escalar» solo oculta de nuevo esos 4 tiradores; mover, cambiar el tamaño y girar "
138
+ + "siguen funcionando sobre la forma actual exacta de la imagen en ambos casos.",
139
+ helpImageRemove: "Haga clic en su tirador × para quitarla.",
140
+ };