Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Auto-Translator widget #399

Open
wants to merge 75 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
2213136
Adding weather ingest parser
bzn-cp Jul 18, 2024
4c2a88e
Merge branch 'superdesk:develop' into develop
cpemichalhorak Jul 19, 2024
672f7a3
Merge branch 'develop' of https://github.com/superdesk/superdesk-cp i…
cmuldur Aug 26, 2024
7e0dd97
Merge branch 'develop' of https://github.com/superdesk/superdesk-cp i…
cmuldur Sep 13, 2024
abc2dc5
intial commit
koksoya Oct 14, 2024
2b374a4
initial version of translation methods added
koksoya Oct 16, 2024
f4f4b67
basic advanced and deepl translations added
koksoya Oct 16, 2024
e8051a6
initial setup
txl-cp Aug 30, 2024
703f433
fix client docker build
txl-cp Sep 3, 2024
829dd3f
fix client racing in Dockerfile
txl-cp Sep 3, 2024
4ac5d2d
add client dockerignore
txl-cp Sep 4, 2024
3a2cfd6
initial add translation widget
txl-cp Sep 3, 2024
f6e65a2
add initial translate widget dialog
txl-cp Sep 4, 2024
944981d
initial mockup
txl-cp Sep 4, 2024
8285dd3
mockup v1
txl-cp Sep 4, 2024
d059236
add create desk workflow, stylings of mockup
txl-cp Sep 5, 2024
56e8635
add formik forms
txl-cp Oct 17, 2024
5a3afcf
apply translation to working editor
txl-cp Oct 17, 2024
c33735f
fix server docker setup
txl-cp Oct 18, 2024
83e5d48
App secrets are moved to env file.
koksoya Oct 22, 2024
8bce0f8
Merge remote-tracking branch 'origin/translation-widget-v3' into tran…
koksoya Oct 22, 2024
d1787f6
Converted char references to corresponding unicode chars in responses
koksoya Oct 23, 2024
f5c78f1
Merge pull request #3 from canadianpress/translation-widget-v3
koksoya Oct 23, 2024
5ffb969
Merge branch 'translation-widget' of https://github.com/canadianpress…
koksoya Oct 23, 2024
5345c19
add components
txl-cp Oct 18, 2024
22b9405
fix translation article button
txl-cp Oct 21, 2024
d112c0c
add translate api
txl-cp Oct 24, 2024
8cb4643
Merge pull request #4 from canadianpress/translation-widget-client
txl-cp Oct 24, 2024
02d3849
Accepting nested objects for translation inside payload object.
koksoya Oct 28, 2024
6d977da
Merge pull request #5 from canadianpress/translation-widget-v3
koksoya Oct 28, 2024
6702f8b
add photo caption to widget form
txl-cp Oct 28, 2024
f8039cb
add language select
txl-cp Oct 30, 2024
a1a3e00
Merge pull request #6 from canadianpress/translation-widget-client
txl-cp Oct 30, 2024
d2e335a
add loading indicator to button
txl-cp Oct 31, 2024
0f45a3e
change isArticle logic
txl-cp Oct 31, 2024
fd08c31
formatting
txl-cp Oct 31, 2024
69c23eb
add version select
txl-cp Nov 1, 2024
5c11294
add current version label
txl-cp Nov 1, 2024
73b72e4
add constants
txl-cp Nov 4, 2024
ffd32a3
add en/fr translations
txl-cp Nov 4, 2024
beca4a6
add content-type
txl-cp Nov 4, 2024
0dc8251
Merge pull request #7 from canadianpress/translation-widget-client
txl-cp Nov 5, 2024
508e516
add isLoading to submit button
txl-cp Nov 7, 2024
d344eaf
change writethru to anpa_take_key
txl-cp Nov 7, 2024
c964d96
add current working editor to writethrus
txl-cp Nov 7, 2024
93c4ca6
hide images from widget
txl-cp Nov 7, 2024
5e7c447
rename article variable
txl-cp Nov 8, 2024
755e31c
Merge pull request #8 from canadianpress/translation-widget-client
txl-cp Nov 8, 2024
5951f39
add writethru comparison
txl-cp Nov 8, 2024
c788bff
fix getWritethrus request
txl-cp Nov 8, 2024
fdffc60
improve key uniqueness for version map
txl-cp Nov 8, 2024
826a317
add sanitize and custom pretty diff
txl-cp Nov 9, 2024
1660329
fix format of custom pretty diff
txl-cp Nov 11, 2024
a617514
remove log
txl-cp Nov 11, 2024
76bd3f3
feedback
txl-cp Nov 11, 2024
a28331d
add fn for compare content values
txl-cp Nov 11, 2024
a611b34
Merge pull request #9 from canadianpress/translation-widget-client
txl-cp Nov 11, 2024
2ba2ca5
make compare accordion accessible
txl-cp Nov 11, 2024
6cc661c
Merge pull request #10 from canadianpress/translation-widget-client
txl-cp Nov 11, 2024
68a1db4
improve aria, remove unused aria
txl-cp Nov 12, 2024
81dbb5e
Merge pull request #11 from canadianpress/translation-widget-client
txl-cp Nov 12, 2024
15ce7e9
remove ai widget
cmuldur Jan 20, 2025
981c0a8
Merge pull request #12 from canadianpress/translation-widget-client
cmuldur Jan 20, 2025
a1d8de1
constant commented out
cmuldur Jan 31, 2025
3e59d70
style: black format fix
cmuldur Feb 3, 2025
ecbad64
Merge branch 'develop' into translation-widget
cmuldur Feb 3, 2025
ab6baf4
style: flake8 fix
cmuldur Feb 3, 2025
88b39f7
refactor: mypy errors fix
cmuldur Feb 3, 2025
c2e5fe4
refactor: mypy error fix
cmuldur Feb 3, 2025
73b4e4a
add type-safe formik, add dynamic form fields, refactor form componen…
txl-cp Feb 24, 2025
46b863a
Merge pull request #13 from canadianpress/translation-widget-client
txl-cp Feb 24, 2025
feb24d4
fix language detection for translation from/to selects
txl-cp Feb 25, 2025
71f8db8
Merge pull request #14 from canadianpress/dev-1408
txl-cp Feb 25, 2025
48b3000
Merge pull request #15 from canadianpress/translation-widget-client
txl-cp Feb 25, 2025
be5a776
Merge branch 'develop' into translation-widget
petrjasek Feb 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,032 changes: 3,032 additions & 0 deletions client/extensions/auto-translator/package-lock.json

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions client/extensions/auto-translator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"private": true,
"name": "auto-translator",
"version": "1.0.0",
"description": "Translation Widget",
"main": "dist/src/extension.js",
"scripts": {
"compile": "tsc -p src",
"compile-e2e": "echo 'No end-to-end tests defined'",
"watch": "tsc -watch -p src"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/diff-match-patch": "1.0.36",
"@types/dompurify": "3.0.5",
"superdesk-code-style": "1.3.0",
"typescript": "4.9.5"
},
"dependencies": {
"diff-match-patch": "1.0.5",
"formik": "2.4.6",
"superdesk-ui-framework": "3.1.18"
},
"superdeskExtension": {
"translations-extract-paths": [
"./src"
],
"translations-directory": "translations"
}
}
86 changes: 86 additions & 0 deletions client/extensions/auto-translator/src/components/buton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from "react";
import {
Spinner,
Button as SuperdeskButton,
} from "superdesk-ui-framework/react";

type SuperdeskButtonProps = React.ComponentProps<typeof SuperdeskButton>;

type SuperdeskButtonStyles =
| Exclude<
SuperdeskButtonProps[keyof Pick<
SuperdeskButtonProps,
"size" | "type" | "style" | "theme"
>],
undefined | "light"
>
| "expand"
| "disabled"
| "iconOnly"
| "iconOnlyCircle";

const superdeskButtonClasses: Record<SuperdeskButtonStyles, string> = {
expand: "btn--expanded",
small: "btn--small",
normal: "btn--normal",
large: "btn--large",
default: "btn--default",
primary: "btn--primary",
success: "btn--success",
warning: "btn--warning",
alert: "btn--alert",
highlight: "btn--highlight",
"sd-green": "btn--sd-green",
filled: "btn--filled",
hollow: "btn--hollow",
"text-only": "btn--text-only",
disabled: "btn--disabled",
iconOnly: "btn--icon-only",
dark: "btn--ui-dark",
iconOnlyCircle: "btn--icon-only-circle",
};

type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
label: React.ReactNode;
superdeskButtonProps?: Partial<SuperdeskButtonProps>;
};

export const Button = ({
label,
superdeskButtonProps,
className,
...props
}: ButtonProps) => {
const classes = ["btn"];

if (superdeskButtonProps?.expand) classes.push(superdeskButtonClasses.expand);
if (superdeskButtonProps?.size)
classes.push(superdeskButtonClasses[superdeskButtonProps.size]);
if (superdeskButtonProps?.type)
classes.push(superdeskButtonClasses[superdeskButtonProps.type]);
if (superdeskButtonProps?.style)
classes.push(superdeskButtonClasses[superdeskButtonProps.style]);
if (superdeskButtonProps?.disabled)
classes.push(superdeskButtonClasses.disabled);
if (superdeskButtonProps?.iconOnly)
classes.push(superdeskButtonClasses.iconOnly);
if (superdeskButtonProps?.theme === "dark")
classes.push(superdeskButtonClasses.dark);
if (superdeskButtonProps?.shape === "round" && superdeskButtonProps.iconOnly)
classes.push(superdeskButtonClasses.iconOnlyCircle);

if (className) classes.push(className);

return (
<button
type="button"
className={classes.join(" ")}
disabled={
superdeskButtonProps?.disabled || superdeskButtonProps?.isLoading
}
{...props}
>
{superdeskButtonProps?.isLoading ? <Spinner size="mini" /> : label}
</button>
);
};
14 changes: 14 additions & 0 deletions client/extensions/auto-translator/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Button } from "./buton";
import { FormSelect, Select } from "./select";
import { FormTextEditorInput, TextEditorInput } from "./text-editor-input";
import { FormTextInput, TextInput } from "./text-input";

export {
Button,
FormSelect,
FormTextEditorInput,
FormTextInput,
Select,
TextEditorInput,
TextInput,
};
56 changes: 56 additions & 0 deletions client/extensions/auto-translator/src/components/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useField } from "formik";
import * as React from "react";

type SelectProps = React.InputHTMLAttributes<HTMLSelectElement> & {
label: string;
field?: any;
meta?: any;
[key: string]: any;
};

export const Select = ({
children,
label,
field,
meta,
...props
}: SelectProps) => {
return (
<div className="sd-input sd-input--medium">
<label className="sd-input__label" htmlFor={label} id={`${label}label`}>
{label}
</label>
<div className="sd-input__input-container">
<span className="sd-input__select-caret-wrapper">
<select
{...field}
{...props}
aria-describedby={`${label}label`}
className="sd-input__select"
id={label}
>
{children}
</select>
</span>
</div>
{meta?.error && <div className="sd-input__message-box">{meta.error}</div>}
</div>
);
};

type FormSelectProps = SelectProps & { name: string };

export const FormSelect = ({
name,
label,
children,
...props
}: FormSelectProps) => {
const [field, meta] = useField(name);

return (
<Select label={label} field={field} meta={meta} {...props}>
{children}
</Select>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useField } from "formik";
import * as React from "react";
import { InputWrapper } from "superdesk-ui-framework/react";
import { superdesk } from "../superdesk";

type TextEditorInputProps = {
label: string;
value: string;
onChange: (value: string) => void;
[key: string]: any;
};

export const TextEditorInput = ({
label,
value,
onChange,
...props
}: TextEditorInputProps) => {
const { Editor3Html } = superdesk.components;

return (
<InputWrapper label={label} fullWidth boxedStyle boxedLable>
<Editor3Html
key={label}
readOnly={false}
value={value}
onChange={onChange}
{...props}
/>
</InputWrapper>
);
};

type FormTextEditorInputProps = Omit<
TextEditorInputProps,
"value" | "onChange"
> & { name: string };

export const FormTextEditorInput = ({
label,
name,
...props
}: FormTextEditorInputProps) => {
// @ts-ignore
const [field, meta, helpers] = useField(name);
const { setValue } = helpers;

return (
<TextEditorInput
label={label}
value={field.value}
onChange={(value) => {
setValue(value);
}}
{...props}
/>
);
};
45 changes: 45 additions & 0 deletions client/extensions/auto-translator/src/components/text-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useField } from "formik";
import * as React from "react";

type TextInputProps = React.InputHTMLAttributes<HTMLInputElement> & {
label: string;
field?: any;
meta?: any;
[key: string]: any;
};

export const TextInput = ({ label, field, meta, ...props }: TextInputProps) => {
return (
<div className="sd-input sd-input--medium sd-input--boxed-style sd-input--boxed-label">
<label
className="sd-input__label sd-input__label--boxed"
htmlFor={label}
id={`${label}label`}
>
{label}
</label>
<div className="sd-input__input-container">
<input
className="sd-input__input"
{...field}
{...props}
type="text"
aria-describedby={`${label}label`}
/>
</div>
{meta?.error && <div className="sd-input__message-box">{meta.error}</div>}
</div>
);
};

type FormTextInputProps = TextInputProps & { name: string };

export const FormTextInput = ({
name,
label,
...props
}: FormTextInputProps) => {
const [field, meta] = useField(name);

return <TextInput label={label} field={field} meta={meta} {...props} />;
};
47 changes: 47 additions & 0 deletions client/extensions/auto-translator/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const WIDGET_ID = "auto-translator-widget" as const;

const TRANSLATION_TYPES = {
basic: "Google Basic",
advanced_nmt: "Google NMT",
// advanced_llm: "Google LLM",
deepl: "DeepL",
} as const;

const TRANSLATION_LANGUAGES = {
en: "english",
fr: "french",
} as const;

const TRANSLATION_VERSIONS = {
original: "original",
aiTranslation: "ai translation",
manualTranslation: "manual translation",
} as const;

// https://www.andiamo.co.uk/resources/iso-language-codes/
const TRANSLATION_LANGUAGES_CODES_MAP = {
en: "en",
"en-au": "en",
"en-bz": "en",
"en-ca": "en",
"en-ie": "en",
"en-jm": "en",
"en-nz": "en",
"en-za": "en",
"en-tt": "en",
"en-gb": "en",
"en-us": "en",
fr: "fr",
"fr-be": "fr",
"fr-ca": "fr",
"fr-lu": "fr",
"fr-ch": "fr",
} as const;

export {
WIDGET_ID,
TRANSLATION_TYPES,
TRANSLATION_LANGUAGES,
TRANSLATION_VERSIONS,
TRANSLATION_LANGUAGES_CODES_MAP,
};
36 changes: 36 additions & 0 deletions client/extensions/auto-translator/src/extension.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
IArticle,
IExtension,
IExtensionActivationResult,
} from "superdesk-api";
import { WIDGET_ID } from "./constants";
import { superdesk } from "./superdesk";
import { capitalize } from "./utilities";
import { AutoTranslatorWidget } from "./widget";

const extension: IExtension = {
activate: () => {
const { gettext } = superdesk.localization;

const result: IExtensionActivationResult = {
contributions: {
authoringSideWidgets: [
{
_id: WIDGET_ID,
label: `${capitalize(gettext("auto"))} ${capitalize(
gettext("translate")
)}`,
icon: "multiedit",
order: 1,
component: AutoTranslatorWidget,
isAllowed: (item: IArticle) => item.type === "text",
},
],
},
};

return Promise.resolve(result);
},
};

export default extension;
7 changes: 7 additions & 0 deletions client/extensions/auto-translator/src/superdesk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ISuperdesk } from "superdesk-api";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
export const superdesk = window["extensionsApiInstances"][
"auto-translator"
] as ISuperdesk;
7 changes: 7 additions & 0 deletions client/extensions/auto-translator/src/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "superdesk-code-style/tsconfig-strict",
"compilerOptions": {
"outDir": "../dist/src",
"esModuleInterop": true
}
}
Loading