Localize hard strings in submission & performer tagger (#2304)

* Localize hard strings in submission & performer tagger
* Localise show/hide configuration

Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
Still Hsu 2022-02-16 08:41:08 +08:00 committed by GitHub
parent 7336a6cd18
commit da600d19d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 223 additions and 64 deletions

View file

@ -4,6 +4,7 @@ import { Button, Form } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { Modal } from "src/components/Shared";
import { getStashboxBase } from "src/utils";
import { FormattedMessage, useIntl } from "react-intl";
interface IProps {
show: boolean;
@ -42,6 +43,7 @@ export const SubmitStashBoxDraft: React.FC<IProps> = ({
query
);
const [selectedBox, setSelectedBox] = useState(0);
const intl = useIntl();
const handleSubmit = () => {
submit({
@ -62,7 +64,7 @@ export const SubmitStashBoxDraft: React.FC<IProps> = ({
return (
<Modal
icon="paper-plane"
header="Submit to Stash-Box"
header={intl.formatMessage({ id: "actions.submit_stash_box" })}
isRunning={loading}
show={show}
accept={{
@ -73,7 +75,7 @@ export const SubmitStashBoxDraft: React.FC<IProps> = ({
<>
<Form.Group className="form-row align-items-end">
<Form.Label className="col-6">
Selected Stash-Box endpoint:
<FormattedMessage id="stashbox.selected_stash_box" />:
</Form.Label>
<Form.Control
as="select"
@ -88,12 +90,15 @@ export const SubmitStashBoxDraft: React.FC<IProps> = ({
</Form.Control>
</Form.Group>
<Button onClick={handleSubmit}>
Submit {`"${entity.name ?? entity.title}"`}
<FormattedMessage id="actions.submit" />{" "}
{`"${entity.name ?? entity.title}"`}
</Button>
</>
) : (
<>
<h6>Submission successful</h6>
<h6>
<FormattedMessage id="stashbox.submission_successful" />
</h6>
<div>
<a
target="_blank"
@ -102,14 +107,19 @@ export const SubmitStashBoxDraft: React.FC<IProps> = ({
boxes[selectedBox].endpoint
)}drafts/${getResponseId(data)}`}
>
Go to {boxes[selectedBox].name} to review draft.
<FormattedMessage
id="stashbox.go_review_draft"
values={{ endpoint_name: boxes[selectedBox].name }}
/>
</a>
</div>
</>
)}
{error !== undefined && (
<>
<h6 className="mt-2">Submission failed</h6>
<h6 className="mt-2">
<FormattedMessage id="stashbox.submission_failed" />
</h6>
<div>{error.message}</div>
</>
)}

View file

@ -1,5 +1,6 @@
import React, { Dispatch, useState } from "react";
import { Badge, Button, Card, Collapse, Form } from "react-bootstrap";
import { FormattedMessage } from "react-intl";
import { ConfigurationContext } from "src/hooks/Config";
import { TextUtils } from "src/utils";
@ -38,28 +39,34 @@ const Config: React.FC<IConfigProps> = ({ show, config, setConfig }) => {
<Collapse in={show}>
<Card>
<div className="row">
<h4 className="col-12">Configuration</h4>
<h4 className="col-12">
<FormattedMessage id="configuration" />
</h4>
<hr className="w-100" />
<div className="col-md-6">
<Form.Group controlId="excluded-performer-fields">
<h6>Excluded fields:</h6>
<h6>
<FormattedMessage id="performer_tagger.config.excluded_fields" />
</h6>
<span>
{excludedFields.length > 0
? excludedFields.map((f) => (
<Badge variant="secondary" className="tag-item" key={f}>
{TextUtils.capitalize(f)}
</Badge>
))
: "No fields are excluded"}
{excludedFields.length > 0 ? (
excludedFields.map((f) => (
<Badge variant="secondary" className="tag-item" key={f}>
{TextUtils.capitalize(f)}
</Badge>
))
) : (
<FormattedMessage id="performer_tagger.config.no_fields_are_excluded" />
)}
</span>
<Form.Text>
These fields will not be changed when updating performers.
<FormattedMessage id="performer_tagger.config.these_fields_will_not_be_changed_when_updating_performers" />
</Form.Text>
<Button
onClick={() => setShowExclusionModal(true)}
className="mt-2"
>
Edit Excluded Fields
<FormattedMessage id="performer_tagger.config.edit_excluded_fields" />
</Button>
</Form.Group>
<Form.Group
@ -67,7 +74,7 @@ const Config: React.FC<IConfigProps> = ({ show, config, setConfig }) => {
className="align-items-center row no-gutters mt-4"
>
<Form.Label className="mr-4">
Active stash-box instance:
<FormattedMessage id="performer_tagger.config.active_stash-box_instance" />
</Form.Label>
<Form.Control
as="select"
@ -76,7 +83,11 @@ const Config: React.FC<IConfigProps> = ({ show, config, setConfig }) => {
disabled={!stashBoxes.length}
onChange={handleInstanceSelect}
>
{!stashBoxes.length && <option>No instances found</option>}
{!stashBoxes.length && (
<option>
<FormattedMessage id="performer_tagger.config.no_instances_found" />
</option>
)}
{stashConfig?.general.stashBoxes.map((i) => (
<option value={i.endpoint} key={i.endpoint}>
{i.endpoint}

View file

@ -110,7 +110,9 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
setSearchResults(results);
setSearchErrors({
...searchErrors,
[performerID]: "Network Error",
[performerID]: intl.formatMessage({
id: "performer_tagger.network_error",
}),
});
});
@ -178,11 +180,16 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
setError({
...error,
[performerID]: {
message: `Failed to save performer "${modalPerformer?.name}"`,
message: intl.formatMessage(
{ id: "performer_tagger.failed_to_save_performer" },
{ performer: modalPerformer?.name }
),
details:
res?.errors?.[0].message ===
"UNIQUE constraint failed: performers.checksum"
? "Name already exists"
? intl.formatMessage({
id: "performer_tagger.name_already_exists",
})
: res?.errors?.[0].message,
},
});
@ -198,7 +205,9 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
if (!isTagged && hasStashIDs) {
mainContent = (
<div className="text-left">
<h5 className="text-bold">Performer already tagged</h5>
<h5 className="text-bold">
<FormattedMessage id="performer_tagger.performer_already_tagged" />
</h5>
</div>
);
} else if (!isTagged && !hasStashIDs) {
@ -239,7 +248,9 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
} else if (isTagged) {
mainContent = (
<div className="d-flex flex-column text-left">
<h5>Performer successfully tagged:</h5>
<h5>
<FormattedMessage id="performer_tagger.performer_successfully_tagged" />
</h5>
<h6>
<Link className="bold" to={`/performers/${performer.id}`}>
{taggedPerformers[performer.id].name}
@ -285,7 +296,7 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
{loadingUpdate === stashID.stash_id ? (
<LoadingIndicator inline small message="" />
) : (
"Refresh"
<FormattedMessage id="actions.refresh" />
)}
</Button>
)}
@ -312,7 +323,9 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
);
} else if (searchResults[performer.id]?.length === 0) {
subContent = (
<div className="text-danger font-weight-bold">No results found.</div>
<div className="text-danger font-weight-bold">
<FormattedMessage id="performer_tagger.no_results_found" />
</div>
);
}
@ -340,7 +353,9 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
onSave={handlePerformerUpdate}
excludedPerformerFields={config.excludedPerformerFields}
icon="tags"
header="Update Performer"
header={intl.formatMessage({
id: "performer_tagger.update_performer",
})}
endpoint={selectedEndpoint.endpoint}
/>
)}
@ -367,8 +382,15 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
<Modal
show={showBatchUpdate}
icon="tags"
header="Update Performers"
accept={{ text: "Update Performers", onClick: handleBatchUpdate }}
header={intl.formatMessage({
id: "performer_tagger.update_performers",
})}
accept={{
text: intl.formatMessage({
id: "performer_tagger.update_performers",
}),
onClick: handleBatchUpdate,
}}
cancel={{
text: intl.formatMessage({ id: "actions.cancel" }),
variant: "danger",
@ -378,13 +400,15 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
>
<Form.Group>
<Form.Label>
<h6>Performer selection</h6>
<h6>
<FormattedMessage id="performer_tagger.performer_selection" />
</h6>
</Form.Label>
<Form.Check
id="query-page"
type="radio"
name="performer-query"
label="Current page"
label={<FormattedMessage id="performer_tagger.current_page" />}
defaultChecked
onChange={() => setQueryAll(false)}
/>
@ -392,53 +416,71 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
id="query-all"
type="radio"
name="performer-query"
label="All performers in the database"
label={intl.formatMessage({
id: "performer_tagger.query_all_performers_in_the_database",
})}
defaultChecked={false}
onChange={() => setQueryAll(true)}
/>
</Form.Group>
<Form.Group>
<Form.Label>
<h6>Tag Status</h6>
<h6>
<FormattedMessage id="performer_tagger.tag_status" />
</h6>
</Form.Label>
<Form.Check
id="untagged-performers"
type="radio"
name="performer-refresh"
label="Untagged performers"
label={intl.formatMessage({
id: "performer_tagger.untagged_performers",
})}
defaultChecked
onChange={() => setRefresh(false)}
/>
<Form.Text>
Updating untagged performers will try to match any performers that
lack a stashid and update the metadata.
<FormattedMessage id="performer_tagger.updating_untagged_performers_description" />
</Form.Text>
<Form.Check
id="tagged-performers"
type="radio"
name="performer-refresh"
label="Refresh tagged performers"
label={intl.formatMessage({
id: "performer_tagger.refresh_tagged_performers",
})}
defaultChecked={false}
onChange={() => setRefresh(true)}
/>
<Form.Text>
Refreshing will update the data of any tagged performers from the
stash-box instance.
<FormattedMessage id="performer_tagger.refreshing_will_update_the_data" />
</Form.Text>
</Form.Group>
<b>{`${
queryAll
? allPerformers?.findPerformers.count
: performers.filter((p) =>
refresh ? p.stash_ids.length > 0 : p.stash_ids.length === 0
).length
} performers will be processed`}</b>
<b>
<FormattedMessage
id="performer_tagger.number_of_performers_will_be_processed"
values={{
performer_count: queryAll
? allPerformers?.findPerformers.count
: performers.filter((p) =>
refresh ? p.stash_ids.length > 0 : p.stash_ids.length === 0
).length,
}}
/>
</b>
</Modal>
<Modal
show={showBatchAdd}
icon="star"
header="Add New Performers"
accept={{ text: "Add Performers", onClick: handleBatchAdd }}
header={intl.formatMessage({
id: "performer_tagger.add_new_performers",
})}
accept={{
text: intl.formatMessage({
id: "performer_tagger.add_new_performers",
}),
onClick: handleBatchAdd,
}}
cancel={{
text: intl.formatMessage({ id: "actions.cancel" }),
variant: "danger",
@ -450,20 +492,21 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
className="text-input"
as="textarea"
ref={performerInput}
placeholder="Performer names separated by comma"
placeholder={intl.formatMessage({
id: "performer_tagger.performer_names_separated_by_comma",
})}
rows={6}
/>
<Form.Text>
Any names entered will be queried from the remote Stash-Box instance
and added if found. Only exact matches will be considered a match.
<FormattedMessage id="performer_tagger.any_names_entered_will_be_queried" />
</Form.Text>
</Modal>
<div className="ml-auto mb-3">
<Button onClick={() => setShowBatchAdd(true)}>
Batch Add Performers
<FormattedMessage id="performer_tagger.batch_add_performers" />
</Button>
<Button className="ml-3" onClick={() => setShowBatchUpdate(true)}>
Batch Update Performers
<FormattedMessage id="performer_tagger.batch_update_performers" />
</Button>
</div>
<div className={CLASSNAME}>{renderPerformers()}</div>
@ -477,6 +520,7 @@ interface ITaggerProps {
export const PerformerTagger: React.FC<ITaggerProps> = ({ performers }) => {
const jobsSubscribe = useJobsSubscribe();
const intl = useIntl();
const { configuration: stashConfig } = React.useContext(ConfigurationContext);
const [{ data: config }, setConfig] = useLocalForage<ITaggerConfig>(
LOCAL_FORAGE_KEY,
@ -567,7 +611,9 @@ export const PerformerTagger: React.FC<ITaggerProps> = ({ performers }) => {
: undefined;
return (
<Form.Group className="px-4">
<h5>Status: Tagging performers</h5>
<h5>
<FormattedMessage id="performer_tagger.status_tagging_performers" />
</h5>
{progress !== undefined && (
<ProgressBar
animated
@ -582,12 +628,18 @@ export const PerformerTagger: React.FC<ITaggerProps> = ({ performers }) => {
if (batchJobID !== undefined) {
return (
<Form.Group className="px-4">
<h5>Status: Tagging job queued</h5>
<h5>
<FormattedMessage id="performer_tagger.status_tagging_job_queued" />
</h5>
</Form.Group>
);
}
}
const showHideConfigId = showConfig
? "actions.hide_configuration"
: "actions.show_configuration";
return (
<>
<Manual
@ -601,15 +653,15 @@ export const PerformerTagger: React.FC<ITaggerProps> = ({ performers }) => {
<>
<div className="row mb-2 no-gutters">
<Button onClick={() => setShowConfig(!showConfig)} variant="link">
{showConfig ? "Hide" : "Show"} Configuration
{intl.formatMessage({ id: showHideConfigId })}
</Button>
<Button
className="ml-auto"
onClick={() => setShowManual(true)}
title="Help"
title={intl.formatMessage({ id: "help" })}
variant="link"
>
Help
<FormattedMessage id="help" />
</Button>
</div>
@ -634,8 +686,7 @@ export const PerformerTagger: React.FC<ITaggerProps> = ({ performers }) => {
) : (
<div className="my-4">
<h3 className="text-center mt-4">
To use the performer tagger a stash-box instance needs to be
configured.
<FormattedMessage id="performer_tagger.to_use_the_performer_tagger" />
</h3>
<h5 className="text-center">
Please see{" "}

View file

@ -44,6 +44,7 @@
"generate_thumb_from_current": "Generate thumbnail from current",
"hash_migration": "hash migration",
"hide": "Hide",
"hide_configuration": "Hide Configuration",
"identify": "Identify",
"ignore": "Ignore",
"import": "Import…",
@ -87,6 +88,7 @@
"set_front_image": "Front image…",
"set_image": "Set image…",
"show": "Show",
"show_configuration": "Show Configuration",
"skip": "Skip",
"stop": "Stop",
"submit_stash_box": "Submit to Stash-Box",
@ -99,7 +101,8 @@
"temp_enable": "Enable temporarily…",
"use_default": "Use default",
"view_random": "View Random",
"continue": "Continue"
"continue": "Continue",
"submit": "Submit"
},
"actions_name": "Actions",
"age": "Age",
@ -909,5 +912,46 @@
"url": "URL",
"videos": "Videos",
"weight": "Weight",
"years_old": "years old"
"years_old": "years old",
"stashbox": {
"selected_stash_box": "Selected Stash-Box endpoint",
"submission_successful": "Submission successful",
"submission_failed": "Submission failed",
"go_review_draft": "Go to {endpoint_name} to review draft."
},
"performer_tagger": {
"network_error": "Network Error",
"failed_to_save_performer": "Failed to save performer \"{performer}\"",
"name_already_exists": "Name already exists",
"performer_already_tagged": "Performer already tagged",
"current_page": "Current page",
"performer_successfully_tagged": "Performer successfully tagged:",
"no_results_found": "No results found.",
"update_performer": "Update Performer",
"update_performers": "Update Performers",
"query_all_performers_in_the_database": "All performers in the database",
"tag_status": "Tag Status",
"untagged_performers": "Untagged performers",
"updating_untagged_performers_description": "Updating untagged performers will try to match any performers that lack a stashid and update the metadata.",
"refresh_tagged_performers": "Refresh tagged performers",
"refreshing_will_update_the_data": "Refreshing will update the data of any tagged performers from the stash-box instance.",
"add_new_performers": "Add New Performers",
"to_use_the_performer_tagger": "To use the performer tagger a stash-box instance needs to be configured.",
"performer_selection": "Performer selection",
"performer_names_separated_by_comma": "Performer names separated by comma",
"any_names_entered_will_be_queried": "Any names entered will be queried from the remote Stash-Box instance and added if found. Only exact matches will be considered a match.",
"batch_add_performers": "Batch Add Performers",
"batch_update_performers": "Batch Update Performers",
"status_tagging_performers": "Status: Tagging performers",
"status_tagging_job_queued": "Status: Tagging job queued",
"number_of_performers_will_be_processed": "{performer_count} performers will be processed",
"config": {
"excluded_fields": "Excluded fields:",
"these_fields_will_not_be_changed_when_updating_performers": "These fields will not be changed when updating performers.",
"edit_excluded_fields": "Edit Excluded Fields",
"no_fields_are_excluded": "No fields are excluded",
"active_stash-box_instance": "Active stash-box instance:",
"no_instances_found": "No instances found"
}
}
}

View file

@ -97,7 +97,9 @@
"temp_disable": "暫時關閉…",
"temp_enable": "暫時啟用…",
"use_default": "使用預設選項",
"view_random": "隨機開啟"
"view_random": "隨機開啟",
"submit": "提交",
"submit_stash_box": "提交至 Stash-Box"
},
"actions_name": "動作",
"age": "年齡",
@ -903,5 +905,46 @@
"url": "連結",
"videos": "影片",
"weight": "體重",
"years_old": "歲"
"years_old": "歲",
"stashbox": {
"go_review_draft": "到 {endpoint_name} 預覽草稿。",
"submission_failed": "提交失敗",
"submission_successful": "提交成功",
"selected_stash_box": "已選擇的 Stash-Box 端點"
},
"performer_tagger": {
"add_new_performers": "新增演員",
"any_names_entered_will_be_queried": "如果輸入的名稱有在設定的 Stash-Box 端點上找到的話,則相對應的結果將會被自動新增。只有完全符合的結果才會被視為匹配。",
"batch_update_performers": "大量更新演員",
"batch_add_performers": "大量新增演員",
"current_page": "目前頁面",
"name_already_exists": "名稱已存在",
"failed_to_save_performer": "無法新增演員「{performer}」",
"no_results_found": "找不到結果。",
"network_error": "網路錯誤",
"performer_names_separated_by_comma": "演員名稱 (以逗號分隔)",
"performer_already_tagged": "演員資料早已新增",
"performer_successfully_tagged": "成功新增演員資料:",
"performer_selection": "選取演員",
"query_all_performers_in_the_database": "查詢所有資料庫中的演員",
"refresh_tagged_performers": "重新整理已新增的演員資料",
"status_tagging_job_queued": "狀態:已排成資料標記",
"refreshing_will_update_the_data": "重新整理資料將會把任何現有的演員資料重新與 Stash-Box 端點上的資料同步。",
"status_tagging_performers": "狀態:新增演員資料中",
"tag_status": "標記狀態",
"to_use_the_performer_tagger": "在使用演員標記工具前,請先設定 Stash-Box 端點。",
"update_performer": "更新演員資料",
"untagged_performers": "未標記的演員",
"update_performers": "更新演員",
"updating_untagged_performers_description": "更新未標記的演員將試著把尚有 stashid 的演員在 Stash-Box 上找尋對應的資料,並將其資料加入至本地的 Metadata 中。",
"number_of_performers_will_be_processed": "將自動處理 {performer_count} 個演員",
"config": {
"active_stash-box_instance": "目前使用的 Stash-box",
"edit_excluded_fields": "編輯排除的種類",
"no_fields_are_excluded": "尚無排除任何種類",
"excluded_fields": "已排除種類:",
"these_fields_will_not_be_changed_when_updating_performers": "以下種類的資訊將不會於更新演員時更動。",
"no_instances_found": "尚未設定端點"
}
}
}