;
+};
+
+export type Translation[] = Plugin<{}>[]> = {
+ plugins: Record;
+ editor: {
+ blockOptions: {
+ delete: string;
+ duplicate: string;
+ turnInto: string;
+ copyBlockLink: string;
+ };
+ placeholder: string;
+ };
+ tools: {
+ toolbar: {
+ highlightColor: {
+ text: string;
+ background: string;
+ customColor: string;
+ };
+ linkTitle: string;
+ };
+ link: {
+ target: string;
+ rel: string;
+ update: string;
+ add: string;
+ delete: string;
+ url: string;
+ title: string;
+ additionalProps: string;
+ titlePlaceholder: string;
+ urlPlaceholder: string;
+ targetPlaceholder: string;
+ relPlaceholder: string;
+ };
+ };
+ [key: string]: unknown;
+};
+
+export type Translations = Record;
+
+export type I18nYooEditor = {
+ translations: Translations;
+ language: Keys;
+ defaultLanguage: Keys;
+ languages: Keys[];
+ setLanguage: (lang: Keys) => void;
+ t: (key: string) => string;
+};
diff --git a/packages/core/i18n/src/utils/index.ts b/packages/core/i18n/src/utils/index.ts
new file mode 100644
index 000000000..cb0ff5c3b
--- /dev/null
+++ b/packages/core/i18n/src/utils/index.ts
@@ -0,0 +1 @@
+export {};
diff --git a/packages/core/i18n/tsconfig.json b/packages/core/i18n/tsconfig.json
new file mode 100644
index 000000000..8c3461a8a
--- /dev/null
+++ b/packages/core/i18n/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../../config/tsconfig.base.json",
+ "include": ["react-svg.d.ts", "css-modules.d.ts", "src"],
+ "exclude": ["dist", "src/**/*.test.tsx", "src/**/*.stories.tsx"],
+ "compilerOptions": {
+ "rootDir": "./src",
+ "outDir": "./dist"
+ },
+ "references": [
+ {
+ "path": "../editor"
+ }
+ ]
+}
diff --git a/packages/development/package.json b/packages/development/package.json
index f0c0f7ad8..2b1019e8c 100644
--- a/packages/development/package.json
+++ b/packages/development/package.json
@@ -38,6 +38,7 @@
"@yoopta/table": "*",
"@yoopta/toolbar": "*",
"@yoopta/video": "*",
+ "@yoopta/i18n": "*",
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
diff --git a/packages/development/src/components/FixedToolbar/FixedToolbar.tsx b/packages/development/src/components/FixedToolbar/FixedToolbar.tsx
index 97f3802e1..aec9a8e7e 100644
--- a/packages/development/src/components/FixedToolbar/FixedToolbar.tsx
+++ b/packages/development/src/components/FixedToolbar/FixedToolbar.tsx
@@ -1,14 +1,14 @@
-import Paragraph, { ParagraphCommands } from '@yoopta/paragraph';
-import Embed, { EmbedCommands } from '@yoopta/embed';
-import Image, { ImageCommands } from '@yoopta/image';
-import Link, { LinkCommands } from '@yoopta/link';
-import Callout, { CalloutCommands } from '@yoopta/callout';
+import { ParagraphCommands } from '@yoopta/paragraph';
+import { EmbedCommands } from '@yoopta/embed';
+import { ImageCommands } from '@yoopta/image';
+import { LinkCommands } from '@yoopta/link';
+import { CalloutCommands } from '@yoopta/callout';
+import { TableCommands } from '@yoopta/table';
import { AccordionCommands } from '@yoopta/accordion';
import { TodoListCommands } from '@yoopta/lists';
-import { HeadingOne, HeadingOneCommands, HeadingThree, HeadingTwo } from '@yoopta/headings';
-import Table, { TableCommands } from '@yoopta/table';
-import { Blocks, Elements, YooEditor, Paths, YooptaPathIndex } from '@yoopta/editor';
+import { HeadingOneCommands } from '@yoopta/headings';
+import { Blocks, YooEditor, YooptaPathIndex } from '@yoopta/editor';
type Props = {
editor: YooEditor;
@@ -47,28 +47,42 @@ export const FixedToolbar = ({ editor, DEFAULT_DATA }: Props) => {
}}
className="p-2 text-xs shadow-md border-r hover:bg-[#64748b] hover:text-[#fff]"
>
- Insert Image
+ {/* Insert Image */}
+ {editor.t('editor.blockOptions.turnInto') || 'Turn into'}
+
+
Languages
+
+ {editor.languages.map((lang) => {
+ const isCurrent = lang === editor.language;
+
+ return (
+
+ );
+ })}
+
+
diff --git a/packages/plugins/embed/src/ui/Placeholder.tsx b/packages/plugins/embed/src/ui/Placeholder.tsx
index faa5e1b87..4e9b4ba04 100644
--- a/packages/plugins/embed/src/ui/Placeholder.tsx
+++ b/packages/plugins/embed/src/ui/Placeholder.tsx
@@ -1,10 +1,12 @@
import { useFloating, inline, flip, shift, offset } from '@floating-ui/react';
import { CodeIcon } from '@radix-ui/react-icons';
+import { useYooptaEditor } from '@yoopta/editor';
import { useState } from 'react';
import { EmbedUploader } from './EmbedUploader';
const Placeholder = ({ attributes, children, blockId }) => {
const [isUploaderOpen, setIsUploaderOpen] = useState(false);
+ const editor = useYooptaEditor();
const { refs, floatingStyles } = useFloating({
placement: 'bottom',
@@ -26,7 +28,9 @@ const Placeholder = ({ attributes, children, blockId }) => {
ref={refs.setReference}
>
-
Click to add embed
+
+ {editor.getLabelText('plugins.Embed.options.placeholder.title') || 'Click to add embed'}
+
{isUploaderOpen && (
{
floatingStyles={floatingStyles}
refs={refs}
onClose={() => setIsUploaderOpen(false)}
+ editor={editor}
/>
)}
{children}
diff --git a/packages/plugins/file/src/ui/FileBlockOptions.tsx b/packages/plugins/file/src/ui/FileBlockOptions.tsx
index 66585e074..e0ec52375 100644
--- a/packages/plugins/file/src/ui/FileBlockOptions.tsx
+++ b/packages/plugins/file/src/ui/FileBlockOptions.tsx
@@ -40,13 +40,13 @@ const FileBlockOptions = ({ editor, block, props: fileProps }: Props) => {
- Alignment
+ {editor.getLabelText('plugins.File.options.align') || 'Align'}
- Open
+ {editor.getLabelText('plugins.File.options.openFile') || 'Open'}
diff --git a/packages/plugins/file/src/ui/FilePlaceholderUploader.tsx b/packages/plugins/file/src/ui/FilePlaceholderUploader.tsx
index cf215da9c..266ec276f 100644
--- a/packages/plugins/file/src/ui/FilePlaceholderUploader.tsx
+++ b/packages/plugins/file/src/ui/FilePlaceholderUploader.tsx
@@ -1,4 +1,4 @@
-import { UI } from '@yoopta/editor';
+import { UI, YooEditor } from '@yoopta/editor';
import { CSSProperties } from 'react';
import { FileUploader } from './FileUploader';
@@ -8,11 +8,12 @@ type Props = {
blockId: string;
onClose: () => void;
onSetLoading: (_s: boolean) => void;
+ editor: YooEditor;
};
const { Overlay, Portal } = UI;
-const FilePlaceholderUploader = ({ floatingStyles, refs, onClose, blockId, onSetLoading }: Props) => {
+const FilePlaceholderUploader = ({ editor, floatingStyles, refs, onClose, blockId, onSetLoading }: Props) => {
const getTabStyles = () => ({
borderBottom: '2px solid #2483e2',
});
@@ -28,7 +29,7 @@ const FilePlaceholderUploader = ({ floatingStyles, refs, onClose, blockId, onSet
style={getTabStyles()}
className={`yoopta-button yoo-file-py-[6px] yoo-file-whitespace-nowrap yoo-file-min-w-0 yoo-file-flex-shrink-0 yoo-file-text-[rgb(55,53,47)] yoo-file-relative yoo-file-cursor-pointer yoo-file-user-select-none yoo-file-bg-inherit yoo-file-transition-[height_20ms_ease-in] yoo-file-inline-flex yoo-file-items-center yoo-file-h-full yoo-file-text-[14px] yoo-file-leading-[1.2] yoo-file-px-[8px]`}
>
- Upload
+ {editor.getLabelText('plugins.File.options.placeholder.upload.label') || 'Upload'}
diff --git a/packages/plugins/file/src/ui/FileUploader.tsx b/packages/plugins/file/src/ui/FileUploader.tsx
index b0e7b0aff..e6fd65d1c 100644
--- a/packages/plugins/file/src/ui/FileUploader.tsx
+++ b/packages/plugins/file/src/ui/FileUploader.tsx
@@ -57,7 +57,7 @@ const FileUploader = ({ accept = '', onClose, blockId, onSetLoading }: Props) =>
onChange={onChange}
multiple={false}
/>
- Upload file
+ {editor.getLabelText('plugins.File.options.placeholder.upload.label') || 'Upload file'}
);
diff --git a/packages/plugins/file/src/ui/Placeholder.tsx b/packages/plugins/file/src/ui/Placeholder.tsx
index 05d91d1ab..2dd8ba01c 100644
--- a/packages/plugins/file/src/ui/Placeholder.tsx
+++ b/packages/plugins/file/src/ui/Placeholder.tsx
@@ -1,5 +1,6 @@
import { useFloating, inline, flip, shift, offset } from '@floating-ui/react';
import { FileIcon } from '@radix-ui/react-icons';
+import { useYooptaEditor } from '@yoopta/editor';
import { CSSProperties, useState } from 'react';
import { FilePlaceholderUploader } from './FilePlaceholderUploader';
import { Loader } from './Loader';
@@ -7,6 +8,7 @@ import { Loader } from './Loader';
const Placeholder = ({ attributes, children, blockId }) => {
const [isUploaderOpen, setIsUploaderOpen] = useState(false);
const [loading, setLoading] = useState(false);
+ const editor = useYooptaEditor();
const { refs, floatingStyles } = useFloating({
placement: 'bottom',
@@ -39,7 +41,11 @@ const Placeholder = ({ attributes, children, blockId }) => {
) : (
)}
- {loading ? 'Loading...' : 'Click to add file'}
+
+ {loading
+ ? editor.getLabelText('plugins.File.options.placeholder.loading') || 'Loading...'
+ : editor.getLabelText('plugins.File.options.placeholder.title') || 'Click to add file'}
+
{loading && (
{
refs={refs}
onClose={() => setIsUploaderOpen(false)}
onSetLoading={onSetLoading}
+ editor={editor}
/>
)}
{children}
diff --git a/packages/plugins/image/src/ui/EmbedUploader.tsx b/packages/plugins/image/src/ui/EmbedUploader.tsx
index e1ac7b85f..f3d47c811 100644
--- a/packages/plugins/image/src/ui/EmbedUploader.tsx
+++ b/packages/plugins/image/src/ui/EmbedUploader.tsx
@@ -26,7 +26,9 @@ const EmbedUploader = ({ blockId, onClose }) => {
{
disabled={isEmpty}
onClick={embed}
>
- Embed image
+ {editor.getLabelText('plugins.Image.options.placeholder.external.button') || 'Embed image'}
);
diff --git a/packages/plugins/image/src/ui/FileUploader.tsx b/packages/plugins/image/src/ui/FileUploader.tsx
index b09aab0c9..8e7a4219a 100644
--- a/packages/plugins/image/src/ui/FileUploader.tsx
+++ b/packages/plugins/image/src/ui/FileUploader.tsx
@@ -66,7 +66,7 @@ const FileUploader = ({ accept = 'image/*', onClose, blockId, onSetLoading }: Pr
onChange={onChange}
multiple={false}
/>
- Upload image
+ {editor.getLabelText('plugins.Image.options.placeholder.upload.label') || 'Upload image'}
);
diff --git a/packages/plugins/image/src/ui/ImageBlockOptions.tsx b/packages/plugins/image/src/ui/ImageBlockOptions.tsx
index fd3129797..2a23649f6 100644
--- a/packages/plugins/image/src/ui/ImageBlockOptions.tsx
+++ b/packages/plugins/image/src/ui/ImageBlockOptions.tsx
@@ -153,7 +153,7 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
- Fit
+ {editor.getLabelText('plugins.Image.options.fit.contain') || 'Fit'}
{imageProps?.fit === 'contain' && (
@@ -164,7 +164,7 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
- Fill
+ {editor.getLabelText('plugins.Image.options.fit.fill') || 'Fill'}
{imageProps?.fit === 'fill' && (
@@ -175,7 +175,7 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
- Cover
+ {editor.getLabelText('plugins.Image.options.fit.cover') || 'Cover'}
{imageProps?.fit === 'cover' && (
@@ -194,6 +194,7 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
refs={refs}
onDelete={onDeleteAltText}
onSave={onSaveAltText}
+ editor={editor}
/>
)}
@@ -204,7 +205,7 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
onClick={() => setIsAltTextOpen(true)}
>
- Alt text
+ {editor.getLabelText('plugins.Image.options.alt.title') || 'Alt text'}
@@ -227,7 +228,7 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
) : (
)}
- Replace image
+ {editor.getLabelText('plugins.Image.options.replaceImage') || 'Replace image'}
@@ -240,13 +241,13 @@ const ImageBlockOptions = ({ editor, block, props: imageProps }: Props) => {
onClick={onToggleAlign}
>
- Alignment
+ {editor.getLabelText('plugins.Image.options.align') || 'Align'}
- Download
+ {editor.getLabelText('plugins.Image.options.downloadImage') || 'Download'}
diff --git a/packages/plugins/image/src/ui/ImageUploader.tsx b/packages/plugins/image/src/ui/ImageUploader.tsx
index 2c809806a..ecbdafbbf 100644
--- a/packages/plugins/image/src/ui/ImageUploader.tsx
+++ b/packages/plugins/image/src/ui/ImageUploader.tsx
@@ -1,4 +1,4 @@
-import { UI } from '@yoopta/editor';
+import { UI, YooEditor } from '@yoopta/editor';
import { CSSProperties, useState } from 'react';
import { EmbedUploader } from './EmbedUploader';
import { FileUploader } from './FileUploader';
@@ -9,12 +9,13 @@ type Props = {
blockId: string;
onClose: () => void;
onSetLoading: (_s: boolean) => void;
+ editor: YooEditor;
};
const { Overlay, Portal } = UI;
type Tab = 'upload' | 'embed';
-const ImageUploader = ({ floatingStyles, refs, onClose, blockId, onSetLoading }: Props) => {
+const ImageUploader = ({ floatingStyles, refs, onClose, blockId, editor, onSetLoading }: Props) => {
const [activeTab, setActiveTab] = useState('upload');
const switchTab = (tab: Tab) => setActiveTab(tab);
@@ -38,7 +39,7 @@ const ImageUploader = ({ floatingStyles, refs, onClose, blockId, onSetLoading }:
style={getTabStyles(isUploader)}
className={`yoopta-button yoo-image-py-[6px] yoo-image-whitespace-nowrap yoo-image-min-w-0 yoo-image-flex-shrink-0 yoo-image-text-[rgb(55,53,47)] yoo-image-relative yoo-image-cursor-pointer yoo-image-user-select-none yoo-image-bg-inherit yoo-image-transition-[height_20ms_ease-in] yoo-image-inline-flex yoo-image-items-center yoo-image-h-full yoo-image-text-[14px] yoo-image-leading-[1.2] yoo-image-px-[8px]`}
>
- Upload
+ {editor.getLabelText('plugins.Image.options.placeholder.upload.label') || 'Upload'}
- Image link
+ {editor.getLabelText('plugins.Image.options.placeholder.external.label') || 'Image link'}
diff --git a/packages/plugins/image/src/ui/InputAltText.tsx b/packages/plugins/image/src/ui/InputAltText.tsx
index b43a0457d..9f445d2ea 100644
--- a/packages/plugins/image/src/ui/InputAltText.tsx
+++ b/packages/plugins/image/src/ui/InputAltText.tsx
@@ -2,7 +2,7 @@ import { UI } from '@yoopta/editor';
const { Overlay, Portal } = UI;
-const InputAltText = ({ floatingStyles, onClose, refs, value, onChange, onSave, onDelete }) => {
+const InputAltText = ({ editor, floatingStyles, onClose, refs, value, onChange, onSave, onDelete }) => {
return (
@@ -10,7 +10,8 @@ const InputAltText = ({ floatingStyles, onClose, refs, value, onChange, onSave,
diff --git a/packages/plugins/image/src/ui/Placeholder.tsx b/packages/plugins/image/src/ui/Placeholder.tsx
index 859b21b69..869ba32b9 100644
--- a/packages/plugins/image/src/ui/Placeholder.tsx
+++ b/packages/plugins/image/src/ui/Placeholder.tsx
@@ -1,5 +1,6 @@
import { useFloating, inline, flip, shift, offset } from '@floating-ui/react';
import { ImageIcon } from '@radix-ui/react-icons';
+import { useYooptaEditor } from '@yoopta/editor';
import { CSSProperties, useState } from 'react';
import { ImageUploader } from './ImageUploader';
import { Loader } from './Loader';
@@ -7,6 +8,7 @@ import { Loader } from './Loader';
const Placeholder = ({ attributes, children, blockId }) => {
const [isUploaderOpen, setIsUploaderOpen] = useState(false);
const [loading, setLoading] = useState(false);
+ const editor = useYooptaEditor();
const { refs, floatingStyles } = useFloating({
placement: 'bottom',
@@ -40,7 +42,11 @@ const Placeholder = ({ attributes, children, blockId }) => {
) : (
)}
- {loading ? 'Loading...' : 'Click to add image'}
+
+ {loading
+ ? editor.getLabelText('plugins.Image.options.placeholder.loading') || 'Loading...'
+ : editor.getLabelText('plugins.Image.options.placeholder.title') || 'Click to add image'}
+
{loading && (
{
refs={refs}
onClose={() => setIsUploaderOpen(false)}
onSetLoading={onSetLoading}
+ editor={editor}
/>
)}
{children}
diff --git a/packages/plugins/link/src/ui/LinkHoverPreview.tsx b/packages/plugins/link/src/ui/LinkHoverPreview.tsx
index 811692391..936f159b9 100644
--- a/packages/plugins/link/src/ui/LinkHoverPreview.tsx
+++ b/packages/plugins/link/src/ui/LinkHoverPreview.tsx
@@ -115,7 +115,8 @@ const LinkHoverPreview = ({ style, setFloating, element, setHoldLinkTool, blockI
setIsEditLinkToolsOpen((prev) => !prev);
}}
>
- Edit
+ {/* Edit */}
+ {editor.getLabelText('tools.link.edit') || 'Edit'}