Skip to content

Commit

Permalink
feat: add translation settings
Browse files Browse the repository at this point in the history
  • Loading branch information
mary-ext committed Dec 26, 2023
1 parent c1f9a53 commit 06b1145
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 28 deletions.
94 changes: 67 additions & 27 deletions app/desktop/components/settings/settings-views/LanguageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import { preferences } from '~/desktop/globals/settings.ts';
import { Interactive } from '~/com/primitives/interactive.ts';

import Checkbox from '~/com/components/inputs/Checkbox.tsx';
import SelectInput from '~/com/components/inputs/SelectInput.tsx';
import SelectInput, { type SelectItem } from '~/com/components/inputs/SelectInput.tsx';

import ChevronRightIcon from '~/com/icons/baseline-chevron-right.tsx';

import { VIEW_ADDITIONAL_LANGUAGE, useViewRouter } from './_router.tsx';
import { VIEW_ADDITIONAL_LANGUAGE, VIEW_EXCLUDED_TRANSLATION, useViewRouter } from './_router.tsx';

const selectItem = Interactive({
class: `flex items-center justify-between gap-4 px-4 py-3 text-left text-sm`,
Expand All @@ -23,6 +23,33 @@ const LanguageView = () => {
const router = useViewRouter();

const langs = preferences.language;
const trans = preferences.translation;

const availableLanguages: SelectItem[] = [
{
value: 'none',
label: 'None',
},
{
value: 'system',
get label() {
return `Primary system language (${languageNames.of(systemLanguages[0])})`;
},
},
...mapDefined(CODE2S, (code) => {
const eng = languageNamesStrict.of(code);
const native = getNativeLanguageName(code);

if (!eng || !native) {
return;
}

return {
value: code,
label: `${eng}${native !== eng ? ` - ${native}` : ``}`,
};
}),
];

return (
<div class="contents">
Expand All @@ -36,31 +63,7 @@ const LanguageView = () => {

<SelectInput
value={langs.defaultPostLanguage}
options={[
{
value: 'none',
label: 'None',
},
{
value: 'system',
get label() {
return `Primary system language (${languageNames.of(systemLanguages[0])})`;
},
},
...mapDefined(CODE2S, (code) => {
const eng = languageNamesStrict.of(code);
const native = getNativeLanguageName(code);

if (!eng || !native) {
return;
}

return {
value: code,
label: `${eng}${native !== eng ? ` - ${native}` : ``}`,
};
}),
]}
options={availableLanguages}
onChange={(next) => {
langs.defaultPostLanguage = next;
}}
Expand Down Expand Up @@ -132,6 +135,43 @@ const LanguageView = () => {

<ChevronRightIcon class="text-xl text-muted-fg" />
</button>

<hr class="mx-4 mt-1 border-divider" />

<p class="p-4 text-base font-bold leading-5">Content translation</p>

<div class="px-4 py-3">
<label class="flex flex-col gap-2">
<span class="text-sm font-medium leading-6 text-primary">Translate into this language</span>

<SelectInput
value={trans.to}
options={availableLanguages}
onChange={(next) => {
trans.to = next;
}}
/>
</label>
</div>

<button onClick={() => router.move({ type: VIEW_EXCLUDED_TRANSLATION })} class={selectItem}>
<div>
<p>Exclude languages from translation</p>
<p class="text-de text-muted-fg">
{(() => {
const count = trans.exclusions.length;

if (count === 1) {
return `${count} language excluded`;
}

return `${count} languages excluded`;
})()}
</p>
</div>

<ChevronRightIcon class="text-xl text-muted-fg" />
</button>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
VIEW_SUBSCRIBED_LABELERS,
VIEW_TEMPORARY_MUTES,
useViewRouter,
VIEW_EXCLUDED_TRANSLATION,
} from './_router.tsx';

const AccessibilityView = lazy(() => import('./AccessibilityView.tsx'));
Expand All @@ -32,6 +33,7 @@ const TemporaryMutesView = lazy(() => import('./content-filters/TemporaryMutesVi
const KeywordFilterFormView = lazy(() => import('./keyword-filters/KeywordFilterFormView.tsx'));

const AdditionalLanguageView = lazy(() => import('./languages/AdditionalLanguageView.tsx'));
const ExcludedTranslationView = lazy(() => import('./languages/ExcludedTranslationView.tsx'));

const views: Record<ViewType, Component> = {
[VIEW_ACCESSIBILITY]: AccessibilityView,
Expand All @@ -49,6 +51,7 @@ const views: Record<ViewType, Component> = {
[VIEW_KEYWORD_FILTER_FORM]: KeywordFilterFormView,

[VIEW_ADDITIONAL_LANGUAGE]: AdditionalLanguageView,
[VIEW_EXCLUDED_TRANSLATION]: ExcludedTranslationView,
};

const SettingsRouterView = () => {
Expand Down
5 changes: 4 additions & 1 deletion app/desktop/components/settings/settings-views/_router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ export const VIEW_TEMPORARY_MUTES = 10;

// Languages
export const VIEW_ADDITIONAL_LANGUAGE = 11;
export const VIEW_EXCLUDED_TRANSLATION = 12;

export type ViewType =
| typeof VIEW_ACCESSIBILITY
| typeof VIEW_ACCOUNTS
| typeof VIEW_ADDITIONAL_LANGUAGE
| typeof VIEW_APPEARANCE
| typeof VIEW_CONTENT_FILTERS
| typeof VIEW_EXCLUDED_TRANSLATION
| typeof VIEW_HIDDEN_REPOSTERS
| typeof VIEW_KEYWORD_FILTER_FORM
| typeof VIEW_KEYWORD_FILTERS
Expand All @@ -53,7 +55,8 @@ export type View =
// Keyword filter form
| { type: typeof VIEW_KEYWORD_FILTER_FORM; id: string | undefined }
// Language
| { type: typeof VIEW_ADDITIONAL_LANGUAGE };
| { type: typeof VIEW_ADDITIONAL_LANGUAGE }
| { type: typeof VIEW_EXCLUDED_TRANSLATION };

export type ViewParams<T extends ViewType, V = View> = V extends { type: T } ? Omit<V, 'type'> : never;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { For, JSX, createMemo, createSignal } from 'solid-js';

import { getNativeLanguageName, languageNames, languageNamesStrict } from '~/utils/intl/display-names.ts';
import { CODE2S } from '~/utils/intl/languages.ts';
import { model } from '~/utils/input.ts';

import { preferences } from '~/desktop/globals/settings.ts';

import { IconButton } from '~/com/primitives/icon-button.ts';

import SearchInput from '~/com/components/inputs/SearchInput.tsx';

import AddIcon from '~/com/icons/baseline-add.tsx';
import ArrowLeftIcon from '~/com/icons/baseline-arrow-left.tsx';
import DeleteIcon from '~/com/icons/baseline-delete.tsx';

import { VIEW_LANGAUGE, useViewRouter } from '../_router.tsx';

const ExcludedTranslationView = () => {
const router = useViewRouter();

const [search, setSearch] = createSignal('');

const normalizedSearch = createMemo(() => search().trim().toLowerCase());

const exclusions = preferences.translation.exclusions;

return (
<div class="contents">
<div class="flex h-13 shrink-0 items-center gap-2 border-b border-divider px-4">
<button
title="Return to previous screen"
onClick={() => router.move({ type: VIEW_LANGAUGE })}
class={/* @once */ IconButton({ edge: 'left' })}
>
<ArrowLeftIcon />
</button>

<h2 class="grow text-base font-bold">Never offer to translate these languages</h2>
</div>
<div class="flex grow flex-col overflow-y-auto pt-3">
<For each={exclusions}>
{(code, index) => (
<div class="mx-4 flex min-w-0 items-center justify-between gap-4 pb-3">
<div>
<p class="text-sm">{/* @once */ languageNames.of(code)}</p>
<p class="text-de text-muted-fg">{/* @once */ getNativeLanguageName(code)}</p>
</div>
<button
title={`Remove this language`}
onClick={() => {
exclusions.splice(index(), 1);
}}
class={/* @once */ IconButton({ edge: 'right' })}
>
<DeleteIcon />
</button>
</div>
)}
</For>

{(() => {
if (exclusions.length === 0) {
return (
<div class="grid h-13 shrink-0 place-items-center pb-1 text-sm text-muted-fg">
No languages excluded.
</div>
);
}
})()}

<hr class="mx-4 mt-2 border-divider" />

<div class="p-4">
<SearchInput ref={model(search, setSearch)} />
</div>

{CODE2S.map((code) => {
const eng = languageNamesStrict.of(code);
const native = getNativeLanguageName(code);

if (!eng || !native) {
return;
}

const lowerEng = eng.toLowerCase();
const lowerNative = native.toLowerCase();

return (() => {
if (exclusions.includes(code)) {
return;
}

const $search = normalizedSearch();
if ($search && !lowerEng.includes($search) && !lowerNative.includes($search)) {
return;
}

return (
<div class="mx-4 flex min-w-0 items-center justify-between gap-4 pb-3">
<div>
<p class="text-sm">{eng}</p>
<p class="text-de text-muted-fg">{native}</p>
</div>
<button
title={`Add this language`}
onClick={() => {
exclusions.push(code);
}}
class={/* @once */ IconButton({ edge: 'right' })}
>
<AddIcon />
</button>
</div>
);
}) as unknown as JSX.Element;
})}
</div>
</div>
);
};

export default ExcludedTranslationView;

0 comments on commit 06b1145

Please sign in to comment.