mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
feat(studio): a full page says so instead of swallowing the click
This commit is contained in:
@@ -195,7 +195,11 @@ function useElementAdders(page: BookPageSetup) {
|
||||
const els = gridElements(grid, defaultGridBox(page, single))
|
||||
// The page's own cap, as a duplicate keeps it: frames that would make the book unsaveable are not added.
|
||||
const on = useStudioStore.getState().doc?.spreads[active]
|
||||
if (!on || on.elements.length + els.length > MAX_SPREAD_ELEMENTS) return
|
||||
if (!on) return
|
||||
if (on.elements.length + els.length > MAX_SPREAD_ELEMENTS) {
|
||||
useStudioStore.getState().refuseFull()
|
||||
return
|
||||
}
|
||||
commit(d => ({
|
||||
...d,
|
||||
spreads: d.spreads.map((sp, i) => (i !== active ? sp : { ...sp, elements: [...sp.elements, ...els] })),
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { MAX_SPREAD_ELEMENTS } from '@trek/shared';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useTranslation } from '../../i18n';
|
||||
import { useStudioStore } from '../../store/studioStore';
|
||||
import { useToast } from '../shared/Toast';
|
||||
|
||||
/**
|
||||
* What the Studio says about a click it could not carry out. Today one thing:
|
||||
* the page already holds as many elements as a page can, so a sticker, a frame,
|
||||
* a paste or a duplicate was not added. Every path that refuses counts it in the
|
||||
* store, and this says it once per count rather than each path in its own words.
|
||||
*/
|
||||
export function StudioNotices() {
|
||||
const { t } = useTranslation();
|
||||
const toast = useToast();
|
||||
const refused = useStudioStore((s) => s.refusedFull);
|
||||
const seen = useRef(refused);
|
||||
useEffect(() => {
|
||||
if (refused === seen.current) return;
|
||||
seen.current = refused;
|
||||
toast.warning(t('journey.studio.pageFull', { max: MAX_SPREAD_ELEMENTS }));
|
||||
}, [refused, toast, t]);
|
||||
return null;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { StudioExport } from './StudioExport'
|
||||
import { StudioAssistantColumn, StudioPageStage } from './StudioAssistantPanel'
|
||||
import { useStudioAssistant } from './useStudioAssistant'
|
||||
import { StudioHeader } from './StudioHeader'
|
||||
import { StudioNotices } from './StudioNotices'
|
||||
import '../../styles/dashboard.css'
|
||||
import '../../styles/studio.css'
|
||||
import './bookFontFaces'
|
||||
@@ -84,6 +85,7 @@ export default function StudioShell() {
|
||||
aria-label={s.t('journey.studio.title')}
|
||||
>
|
||||
<StudioHeader s={s} onExport={() => setExporting(true)} assistant={assistant} />
|
||||
<StudioNotices />
|
||||
{/* The journey gallery's picker, inside Studio's own layer (#2271). */}
|
||||
{s.providerPick && s.journey && (
|
||||
<ProviderPicker
|
||||
|
||||
@@ -99,7 +99,11 @@ export function StudioTextStyles({
|
||||
const place = (lockup: BookLockup) => {
|
||||
const els = placeLockup(lockup, { page, spread, words, gallery: source.photos, newId: bookElementId });
|
||||
// The page's own cap, as a duplicate keeps it: a set that would make the book unsaveable is not placed.
|
||||
if (!els.length || !spread || spread.elements.length + els.length > MAX_SPREAD_ELEMENTS) return;
|
||||
if (!els.length || !spread) return;
|
||||
if (spread.elements.length + els.length > MAX_SPREAD_ELEMENTS) {
|
||||
useStudioStore.getState().refuseFull();
|
||||
return;
|
||||
}
|
||||
commit((d) => ({
|
||||
...d,
|
||||
spreads: d.spreads.map((sp, i) => (i === active ? addBookLockup(sp, els, bookElementId('g')) : sp)),
|
||||
|
||||
@@ -54,6 +54,7 @@ interface Host {
|
||||
styleClip: BookCopiedStyle | null;
|
||||
commit: (fn: (doc: BookDocument) => BookDocument) => void;
|
||||
apply: (fn: (doc: BookDocument) => BookDocument) => void;
|
||||
refuseFull: () => void;
|
||||
}
|
||||
|
||||
type Set = (partial: Partial<Host> | ((s: Host) => Partial<Host>)) => void;
|
||||
@@ -370,7 +371,11 @@ function panelActions(
|
||||
if (!doc) return;
|
||||
const chosen = spread.elements.filter((e) => ids.includes(e.id));
|
||||
// The page's own cap: copies that would make the book unsaveable are not made.
|
||||
if (!chosen.length || spread.elements.length + chosen.length > MAX_SPREAD_ELEMENTS) return;
|
||||
if (!chosen.length) return;
|
||||
if (spread.elements.length + chosen.length > MAX_SPREAD_ELEMENTS) {
|
||||
get().refuseFull();
|
||||
return;
|
||||
}
|
||||
const copies = chosen.map(
|
||||
(e) =>
|
||||
({
|
||||
|
||||
@@ -35,6 +35,14 @@ interface StudioState extends StudioLayerActions {
|
||||
gestureBase: BookDocument | null
|
||||
/** Elements copied for pasting onto any page of the book (#2316). Kept across page switches. */
|
||||
clipboard: StudioClipboard | null
|
||||
/**
|
||||
* Counts the times something was not added because the page already holds
|
||||
* MAX_SPREAD_ELEMENTS. The Studio says so once per count (StudioNotices), so
|
||||
* a full page no longer swallows a click without a word.
|
||||
*/
|
||||
refusedFull: number
|
||||
/** Notes that an addition was refused because the page is full. */
|
||||
refuseFull: () => void
|
||||
/**
|
||||
* The page a chat card points at, outlined in the page rail (journey-studio.md
|
||||
* 4.2). Not part of the book or its history; opening a book clears it.
|
||||
@@ -147,6 +155,8 @@ export const useStudioStore = create<StudioState>((set, get) => ({
|
||||
future: [],
|
||||
gestureBase: null,
|
||||
clipboard: null,
|
||||
refusedFull: 0,
|
||||
refuseFull: () => set(st => ({ refusedFull: st.refusedFull + 1 })),
|
||||
hintSpread: null,
|
||||
readOnly: false,
|
||||
|
||||
@@ -228,7 +238,11 @@ export const useStudioStore = create<StudioState>((set, get) => ({
|
||||
addElement: (spreadIndex, el) => {
|
||||
// The page's own cap, as paste and duplicate keep it: what would make the book unsaveable is not added.
|
||||
const target = get().doc?.spreads[spreadIndex]
|
||||
if (!target || target.elements.length >= MAX_SPREAD_ELEMENTS) return false
|
||||
if (!target) return false
|
||||
if (target.elements.length >= MAX_SPREAD_ELEMENTS) {
|
||||
get().refuseFull()
|
||||
return false
|
||||
}
|
||||
get().commit(doc =>
|
||||
replaceSpread(doc, spreadIndex, sp => ({ ...sp, elements: [...sp.elements, el] })))
|
||||
/*
|
||||
@@ -268,6 +282,10 @@ export const useStudioStore = create<StudioState>((set, get) => ({
|
||||
const room = Math.max(0, MAX_SPREAD_ELEMENTS - target.elements.length)
|
||||
const source = doc.spreads.find(sp => sp.id === clipboard.fromSpreadId)
|
||||
// A copied group arrives as a group of its own, never as part of one already on the page.
|
||||
if (room === 0) {
|
||||
get().refuseFull()
|
||||
return
|
||||
}
|
||||
const copies = regroupCopies(pastedElements(clipboard, target, doc.page).slice(0, room), source, bookElementId)
|
||||
if (!copies.elements.length) return
|
||||
get().commit(d => replaceSpread(d, spreadIndex, sp => withCopies(sp, copies)))
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { act, render } from '@testing-library/react';
|
||||
import { MAX_SPREAD_ELEMENTS, type BookDocument, type BookElement, type BookSpread } from '@trek/shared';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { StudioNotices } from '../../../src/components/Studio/StudioNotices';
|
||||
import { useStudioStore } from '../../../src/store/studioStore';
|
||||
|
||||
/**
|
||||
* FE-STUDIO-FULL-001 to FE-STUDIO-FULL-004: a page that already holds
|
||||
* MAX_SPREAD_ELEMENTS takes nothing more, and the Studio says so instead of
|
||||
* swallowing the click.
|
||||
*/
|
||||
|
||||
const el = (id: string): BookElement =>
|
||||
({
|
||||
id,
|
||||
kind: 'shape',
|
||||
frame: { x: 0, y: 0, w: 10, h: 10 },
|
||||
rotation: 0,
|
||||
opacity: 1,
|
||||
locked: false,
|
||||
shape: 'rect',
|
||||
fill: '#000000',
|
||||
gradient: 'none',
|
||||
stroke: null,
|
||||
strokeWidth: 0,
|
||||
strokeStyle: 'solid',
|
||||
radius: 0,
|
||||
}) as BookElement;
|
||||
|
||||
const spread = (id: string, elements: BookElement[]): BookSpread => ({
|
||||
id,
|
||||
role: 'inner',
|
||||
background: null,
|
||||
elements,
|
||||
parked: [],
|
||||
entryId: null,
|
||||
});
|
||||
|
||||
const book = (...spreads: BookSpread[]): BookDocument =>
|
||||
({
|
||||
version: 1,
|
||||
title: 'T',
|
||||
page: { preset: 'square-210', pageWidth: 210, pageHeight: 210, bleed: 3, safe: 5 },
|
||||
spreads,
|
||||
}) as BookDocument;
|
||||
|
||||
const full = () => Array.from({ length: MAX_SPREAD_ELEMENTS }, (_, i) => el(`f${i}`));
|
||||
const store = () => useStudioStore.getState();
|
||||
|
||||
beforeEach(() => {
|
||||
store().load(book(spread('full', full()), spread('room', [el('a')])));
|
||||
useStudioStore.setState({ refusedFull: 0, clipboard: null });
|
||||
});
|
||||
|
||||
describe('a full page', () => {
|
||||
it('FE-STUDIO-FULL-001: refuses one more element and counts the refusal', () => {
|
||||
expect(store().addElement(0, el('extra'))).toBe(false);
|
||||
expect(store().doc!.spreads[0].elements).toHaveLength(MAX_SPREAD_ELEMENTS);
|
||||
expect(store().refusedFull).toBe(1);
|
||||
expect(store().addElement(1, el('fits'))).toBe(true);
|
||||
expect(store().refusedFull).toBe(1);
|
||||
});
|
||||
|
||||
it('FE-STUDIO-FULL-002: a paste onto it is refused and counted', () => {
|
||||
useStudioStore.setState({ clipboard: { fromSpreadId: 'room', elements: [el('a')] } });
|
||||
store().paste(0);
|
||||
expect(store().doc!.spreads[0].elements).toHaveLength(MAX_SPREAD_ELEMENTS);
|
||||
expect(store().refusedFull).toBe(1);
|
||||
});
|
||||
|
||||
it('FE-STUDIO-FULL-003: a duplicate on it is refused and counted', () => {
|
||||
store().duplicate(0, ['f0']);
|
||||
expect(store().doc!.spreads[0].elements).toHaveLength(MAX_SPREAD_ELEMENTS);
|
||||
expect(store().refusedFull).toBe(1);
|
||||
});
|
||||
|
||||
it('FE-STUDIO-FULL-004: the Studio says it once per refusal', () => {
|
||||
const addToast = vi.fn();
|
||||
window.__addToast = addToast;
|
||||
render(<StudioNotices />);
|
||||
expect(addToast).not.toHaveBeenCalled();
|
||||
act(() => store().refuseFull());
|
||||
expect(addToast).toHaveBeenCalledTimes(1);
|
||||
expect(addToast.mock.calls[0][1]).toBe('warning');
|
||||
act(() => store().refuseFull());
|
||||
expect(addToast).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
@@ -493,6 +493,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'نسخ للصق في أي صفحة (Ctrl+C)',
|
||||
'journey.studio.paste': 'لصق (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'انسخ شيئًا أولًا، ثم الصقه في أي صفحة',
|
||||
'journey.studio.pageFull':
|
||||
'هذه الصفحة ممتلئة: تحتوي على {max} عنصرًا، وهو الحد الأقصى للصفحة. احذف شيئًا أولًا أو استخدم صفحة أخرى.',
|
||||
'journey.studio.myLayouts': 'تخطيطاتي',
|
||||
'journey.studio.myLayoutsEmpty': 'احتفظ بصفحة رتّبتها، ورتّب صفحات أخرى بالطريقة نفسها. تبقى صورها ونصوصها كما هي.',
|
||||
'journey.studio.saveLayout': 'حفظ هذه الصفحة كتخطيط',
|
||||
|
||||
@@ -510,6 +510,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'İstənilən səhifəyə yapışdırmaq üçün kopyala (Ctrl+C)',
|
||||
'journey.studio.paste': 'Yapışdır (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Əvvəlcə nəyisə kopyalayın, sonra onu istənilən səhifəyə yapışdırın',
|
||||
'journey.studio.pageFull':
|
||||
'Bu səhifə doludur: onda artıq {max} element var, bir səhifəyə bundan çoxu sığmır. Əvvəlcə nəyisə silin və ya başqa səhifədən istifadə edin.',
|
||||
'journey.studio.myLayouts': 'Mənim tərtibatlarım',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Tərtib etdiyiniz səhifəni saxlayın və digər səhifələri eyni şəkildə düzün. Onların fotoları və mətnləri qalır.',
|
||||
|
||||
@@ -466,6 +466,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Copiar, para colar em qualquer página (Ctrl+C)',
|
||||
'journey.studio.paste': 'Colar (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Copie algo primeiro e depois cole em qualquer página',
|
||||
'journey.studio.pageFull':
|
||||
'Esta página está cheia: ela já tem {max} elementos, o máximo de uma página. Remova algo primeiro ou use outra página.',
|
||||
'journey.studio.myLayouts': 'Minhas disposições',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Guarde uma página que você organizou e disponha outras páginas do mesmo jeito. As fotos e os textos delas continuam.',
|
||||
|
||||
@@ -465,6 +465,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Copia, per enganxar a qualsevol pàgina (Ctrl+C)',
|
||||
'journey.studio.paste': 'Enganxa (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Primer copia alguna cosa i després enganxa-la a qualsevol pàgina',
|
||||
'journey.studio.pageFull':
|
||||
'Aquesta pàgina és plena: ja té {max} elements, el màxim que hi cap. Treu-ne algun primer o fes servir una altra pàgina.',
|
||||
'journey.studio.myLayouts': 'Les meves disposicions',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Guarda una pàgina que hagis organitzat i disposa altres pàgines de la mateixa manera. Les seves fotos i textos es mantenen.',
|
||||
|
||||
@@ -470,6 +470,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopírovat pro vložení na libovolnou stranu (Ctrl+C)',
|
||||
'journey.studio.paste': 'Vložit (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Nejprve něco zkopírujte a pak to vložte na libovolnou stranu',
|
||||
'journey.studio.pageFull':
|
||||
'Tato stránka je plná: má už {max} prvků, víc se na stránku nevejde. Nejdřív něco odeberte, nebo použijte jinou stránku.',
|
||||
'journey.studio.myLayouts': 'Moje rozvržení',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Uložte si stranu, kterou jste uspořádali, a rozvrhněte stejně i další strany. Jejich fotky a texty zůstanou.',
|
||||
|
||||
@@ -466,6 +466,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopieren, um es auf einer beliebigen Seite einzufügen (Ctrl+C)',
|
||||
'journey.studio.paste': 'Einfügen (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Kopiere zuerst etwas und füge es dann auf einer beliebigen Seite ein',
|
||||
'journey.studio.pageFull':
|
||||
'Diese Seite ist voll: Sie hat schon {max} Elemente, mehr geht auf einer Seite nicht. Entferne zuerst etwas oder nimm eine andere Seite.',
|
||||
'journey.studio.myLayouts': 'Meine Anordnungen',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Behalte eine Seite, die du angeordnet hast, und ordne andere Seiten genauso an. Ihre Fotos und Texte bleiben erhalten.',
|
||||
|
||||
@@ -463,6 +463,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Copy, to paste on any page (Ctrl+C)',
|
||||
'journey.studio.paste': 'Paste (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Copy something first, then paste it on any page',
|
||||
'journey.studio.pageFull':
|
||||
'This page is full: it holds {max} elements, the most a page can. Remove something first, or use another page.',
|
||||
'journey.studio.myLayouts': 'My layouts',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Keep a page you arranged and lay other pages out the same way. Their photos and words stay.',
|
||||
|
||||
@@ -466,6 +466,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Copiar, para pegar en cualquier página (Ctrl+C)',
|
||||
'journey.studio.paste': 'Pegar (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Copia algo primero y luego pégalo en cualquier página',
|
||||
'journey.studio.pageFull':
|
||||
'Esta página está llena: ya tiene {max} elementos, el máximo que cabe. Quita algo primero o usa otra página.',
|
||||
'journey.studio.myLayouts': 'Mis disposiciones',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Guarda una página que hayas organizado y dispón otras páginas de la misma forma. Sus fotos y textos se mantienen.',
|
||||
|
||||
@@ -464,6 +464,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopeeri, et kleepida mis tahes lehele (Ctrl+C)',
|
||||
'journey.studio.paste': 'Kleebi (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Kopeeri esmalt midagi, siis kleebi see mis tahes lehele',
|
||||
'journey.studio.pageFull':
|
||||
'See leht on täis: sellel on juba {max} elementi, rohkem ühele lehele ei mahu. Eemalda esmalt midagi või kasuta teist lehte.',
|
||||
'journey.studio.myLayouts': 'Minu paigutused',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Salvesta enda kujundatud leht ja paiguta teised lehed samamoodi. Nende fotod ja tekstid jäävad alles.',
|
||||
|
||||
@@ -464,6 +464,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Copier, pour coller sur n’importe quelle page (Ctrl+C)',
|
||||
'journey.studio.paste': 'Coller (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Copiez d’abord quelque chose, puis collez-le sur n’importe quelle page',
|
||||
'journey.studio.pageFull':
|
||||
"Cette page est pleine : elle contient déjà {max} éléments, le maximum pour une page. Retirez d'abord un élément ou utilisez une autre page.",
|
||||
'journey.studio.myLayouts': 'Mes dispositions',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Conservez une page que vous avez agencée et disposez d’autres pages de la même façon. Leurs photos et textes restent.',
|
||||
|
||||
@@ -469,6 +469,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Αντιγραφή, για επικόλληση σε οποιαδήποτε σελίδα (Ctrl+C)',
|
||||
'journey.studio.paste': 'Επικόλληση (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Αντιγράψτε πρώτα κάτι και μετά επικολλήστε το σε οποιαδήποτε σελίδα',
|
||||
'journey.studio.pageFull':
|
||||
'Αυτή η σελίδα είναι γεμάτη: έχει ήδη {max} στοιχεία, το μέγιστο για μια σελίδα. Αφαιρέστε πρώτα κάτι ή χρησιμοποιήστε άλλη σελίδα.',
|
||||
'journey.studio.myLayouts': 'Οι διατάξεις μου',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Κρατήστε μια σελίδα που έχετε διαμορφώσει και στήστε άλλες σελίδες με τον ίδιο τρόπο. Οι φωτογραφίες και τα κείμενά τους παραμένουν.',
|
||||
|
||||
@@ -463,6 +463,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Másolás, bármelyik oldalra beilleszthető (Ctrl+C)',
|
||||
'journey.studio.paste': 'Beillesztés (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Előbb másoljon ki valamit, aztán illessze be bármelyik oldalra',
|
||||
'journey.studio.pageFull':
|
||||
'Ez az oldal megtelt: már {max} elem van rajta, ennél több nem fér egy oldalra. Előbb távolíts el valamit, vagy használj másik oldalt.',
|
||||
'journey.studio.myLayouts': 'Saját elrendezéseim',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Mentsen el egy elrendezett oldalt, és rendezzen el más oldalakat is ugyanígy. Azok fotói és szövegei megmaradnak.',
|
||||
|
||||
@@ -455,6 +455,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Salin, untuk ditempel di halaman mana pun (Ctrl+C)',
|
||||
'journey.studio.paste': 'Tempel (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Salin sesuatu dulu, lalu tempel di halaman mana pun',
|
||||
'journey.studio.pageFull':
|
||||
'Halaman ini penuh: sudah berisi {max} elemen, batas maksimal satu halaman. Hapus sesuatu dulu atau gunakan halaman lain.',
|
||||
'journey.studio.myLayouts': 'Tata letakku',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Simpan halaman yang sudah kamu susun dan tata halaman lain dengan cara yang sama. Foto dan teksnya tetap.',
|
||||
|
||||
@@ -465,6 +465,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Copia, per incollare su qualsiasi pagina (Ctrl+C)',
|
||||
'journey.studio.paste': 'Incolla (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Copia prima qualcosa, poi incollalo su qualsiasi pagina',
|
||||
'journey.studio.pageFull':
|
||||
"Questa pagina è piena: contiene già {max} elementi, il massimo per una pagina. Rimuovi prima qualcosa o usa un'altra pagina.",
|
||||
'journey.studio.myLayouts': 'Le mie disposizioni',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Conserva una pagina che hai sistemato e disponi le altre pagine allo stesso modo. Le loro foto e i testi restano.',
|
||||
|
||||
@@ -453,6 +453,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'コピーして任意のページに貼り付け (Ctrl+C)',
|
||||
'journey.studio.paste': '貼り付け (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'まず何かをコピーしてから、任意のページに貼り付けてください',
|
||||
'journey.studio.pageFull':
|
||||
'このページはいっぱいです。すでに{max}個の要素があり、1ページの上限です。先に何かを削除するか、別のページを使ってください。',
|
||||
'journey.studio.myLayouts': 'マイレイアウト',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'配置したページを保存して、他のページも同じ配置にできます。写真と文章はそのまま残ります。',
|
||||
|
||||
@@ -450,6 +450,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': '복사해서 아무 페이지에나 붙여넣기 (Ctrl+C)',
|
||||
'journey.studio.paste': '붙여넣기 (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': '먼저 무언가를 복사한 뒤 아무 페이지에나 붙여넣으세요',
|
||||
'journey.studio.pageFull':
|
||||
'이 페이지는 가득 찼습니다. 이미 요소가 {max}개 있어 한 페이지의 최대치입니다. 먼저 무언가를 지우거나 다른 페이지를 사용하세요.',
|
||||
'journey.studio.myLayouts': '내 레이아웃',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'배치한 페이지를 저장해 두고 다른 페이지도 같은 방식으로 배치하세요. 사진과 글은 그대로 유지됩니다.',
|
||||
|
||||
@@ -466,6 +466,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopiëren, om op elke pagina te plakken (Ctrl+C)',
|
||||
'journey.studio.paste': 'Plakken (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Kopieer eerst iets en plak het dan op een willekeurige pagina',
|
||||
'journey.studio.pageFull':
|
||||
'Deze pagina is vol: er staan al {max} elementen op, het maximum voor een pagina. Verwijder eerst iets of gebruik een andere pagina.',
|
||||
'journey.studio.myLayouts': 'Mijn indelingen',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Bewaar een pagina die je hebt ingedeeld en deel andere pagina’s op dezelfde manier in. Hun foto’s en teksten blijven staan.',
|
||||
|
||||
@@ -482,6 +482,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopiuj, aby wkleić na dowolnej stronie (Ctrl+C)',
|
||||
'journey.studio.paste': 'Wklej (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Najpierw coś skopiuj, a potem wklej to na dowolnej stronie',
|
||||
'journey.studio.pageFull':
|
||||
'Ta strona jest pełna: ma już {max} elementów, tyle, ile mieści się na stronie. Najpierw coś usuń albo użyj innej strony.',
|
||||
'journey.studio.myLayouts': 'Moje układy',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Zachowaj ułożoną stronę i rozmieść inne strony w ten sam sposób. Ich zdjęcia i teksty pozostaną.',
|
||||
|
||||
@@ -481,6 +481,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Копировать, чтобы вставить на любую страницу (Ctrl+C)',
|
||||
'journey.studio.paste': 'Вставить (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Сначала скопируйте что-нибудь, затем вставьте на любую страницу',
|
||||
'journey.studio.pageFull':
|
||||
'Эта страница заполнена: на ней уже {max} элементов, больше на страницу не помещается. Сначала удалите что-нибудь или используйте другую страницу.',
|
||||
'journey.studio.myLayouts': 'Мои макеты',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Сохраните оформленную страницу и расположите другие страницы так же. Их фото и тексты останутся.',
|
||||
|
||||
@@ -474,6 +474,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopírovať na vloženie na ľubovoľnú stranu (Ctrl+C)',
|
||||
'journey.studio.paste': 'Vložiť (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Najprv niečo skopírujte a potom to vložte na ľubovoľnú stranu',
|
||||
'journey.studio.pageFull':
|
||||
'Táto stránka je plná: má už {max} prvkov, viac sa na stránku nezmestí. Najprv niečo odstráňte alebo použite inú stránku.',
|
||||
'journey.studio.myLayouts': 'Moje rozloženia',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Uložte si stranu, ktorú ste usporiadali, a rozložte rovnako aj ďalšie strany. Ich fotky a texty zostanú.',
|
||||
|
||||
@@ -463,6 +463,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopiera för att klistra in på valfri sida (Ctrl+C)',
|
||||
'journey.studio.paste': 'Klistra in (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Kopiera något först och klistra sedan in det på valfri sida',
|
||||
'journey.studio.pageFull':
|
||||
'Den här sidan är full: den har redan {max} element, det mesta en sida rymmer. Ta bort något först eller använd en annan sida.',
|
||||
'journey.studio.myLayouts': 'Mina layouter',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Spara en sida du har ordnat och lägg upp andra sidor på samma sätt. Deras foton och texter finns kvar.',
|
||||
|
||||
@@ -449,6 +449,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'คัดลอกเพื่อวางในหน้าใดก็ได้ (Ctrl+C)',
|
||||
'journey.studio.paste': 'วาง (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'คัดลอกบางอย่างก่อน แล้ววางในหน้าใดก็ได้',
|
||||
'journey.studio.pageFull':
|
||||
'หน้านี้เต็มแล้ว มีองค์ประกอบ {max} ชิ้น ซึ่งเป็นจำนวนสูงสุดต่อหน้า โปรดลบบางอย่างก่อนหรือใช้หน้าอื่น',
|
||||
'journey.studio.myLayouts': 'เค้าโครงของฉัน',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'เก็บหน้าที่คุณจัดไว้ แล้วจัดหน้าอื่นในแบบเดียวกัน รูปภาพและข้อความของหน้าเหล่านั้นยังคงอยู่',
|
||||
|
||||
@@ -464,6 +464,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Kopyala, herhangi bir sayfaya yapıştırmak için (Ctrl+C)',
|
||||
'journey.studio.paste': 'Yapıştır (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Önce bir şey kopyala, sonra herhangi bir sayfaya yapıştır',
|
||||
'journey.studio.pageFull':
|
||||
'Bu sayfa dolu: zaten {max} öğe var, bir sayfaya sığabilecek en fazla sayı bu. Önce bir şey kaldırın ya da başka bir sayfa kullanın.',
|
||||
'journey.studio.myLayouts': 'Yerleşimlerim',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Düzenlediğin bir sayfayı sakla ve diğer sayfaları da aynı şekilde yerleştir. Fotoğrafları ve metinleri olduğu gibi kalır.',
|
||||
|
||||
@@ -485,6 +485,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Копіювати, щоб вставити на будь-яку сторінку (Ctrl+C)',
|
||||
'journey.studio.paste': 'Вставити (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Спершу щось скопіюйте, потім вставте на будь-яку сторінку',
|
||||
'journey.studio.pageFull':
|
||||
'Ця сторінка заповнена: на ній уже {max} елементів, більше на сторінку не вміщується. Спершу видаліть щось або скористайтеся іншою сторінкою.',
|
||||
'journey.studio.myLayouts': 'Мої макети',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Збережіть сторінку, яку ви впорядкували, і розкладайте інші сторінки так само. Їхні фото й тексти залишаться.',
|
||||
|
||||
@@ -455,6 +455,8 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': 'Sao chép để dán vào trang bất kỳ (Ctrl+C)',
|
||||
'journey.studio.paste': 'Dán (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': 'Hãy sao chép thứ gì đó trước, rồi dán vào trang bất kỳ',
|
||||
'journey.studio.pageFull':
|
||||
'Trang này đã đầy: đã có {max} thành phần, mức tối đa cho một trang. Hãy xóa bớt trước hoặc dùng trang khác.',
|
||||
'journey.studio.myLayouts': 'Bố cục của tôi',
|
||||
'journey.studio.myLayoutsEmpty':
|
||||
'Lưu một trang bạn đã sắp xếp và dàn các trang khác theo cùng cách. Ảnh và chữ của chúng được giữ nguyên.',
|
||||
|
||||
@@ -447,6 +447,7 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': '複製,可貼到任何頁面 (Ctrl+C)',
|
||||
'journey.studio.paste': '貼上 (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': '請先複製內容,再貼到任何頁面',
|
||||
'journey.studio.pageFull': '此頁面已滿:已有 {max} 個元素,達到單頁上限。請先刪除一些內容,或使用其他頁面。',
|
||||
'journey.studio.myLayouts': '我的版面',
|
||||
'journey.studio.myLayoutsEmpty': '保存你排好的頁面,讓其他頁面套用相同的版面。它們的照片與文字會保留。',
|
||||
'journey.studio.saveLayout': '將此頁面儲存為版面',
|
||||
|
||||
@@ -447,6 +447,7 @@ const journey: TranslationStrings = {
|
||||
'journey.studio.copyToPage': '复制,可粘贴到任意页面 (Ctrl+C)',
|
||||
'journey.studio.paste': '粘贴 (Ctrl+V)',
|
||||
'journey.studio.pasteEmpty': '请先复制内容,然后粘贴到任意页面',
|
||||
'journey.studio.pageFull': '此页面已满:已有 {max} 个元素,达到单页上限。请先删除一些内容,或使用其他页面。',
|
||||
'journey.studio.myLayouts': '我的版式',
|
||||
'journey.studio.myLayoutsEmpty': '保存你排好的页面,让其他页面采用相同的排版。它们的照片和文字保持不变。',
|
||||
'journey.studio.saveLayout': '将此页面保存为版式',
|
||||
|
||||
Reference in New Issue
Block a user