Skip to content

Commit dc44c84

Browse files
authored
Merge pull request #407 from Constrat/feat/i18n-operators
feat: i18n operator names
2 parents b0da401 + 9ece3a6 commit dc44c84

14 files changed

+690
-125
lines changed

scripts/shared.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fetch from 'node-fetch'
44
import { pinyin } from 'pinyin'
55
import simplebig from 'simplebig'
66

7-
type Profession = { id: string; name: string }
7+
type Profession = { id: string; name: string; name_en?: string }
88
type Professions = (Profession & { sub: Profession[] })[]
99

1010
export async function fileExists(file: string) {
@@ -48,11 +48,14 @@ function transformOperatorName(name: string) {
4848
}
4949
}
5050

51-
const CHARACTER_TABLE_JSON_URL =
51+
const CHARACTER_TABLE_JSON_URL_CN =
5252
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData/master/zh_CN/gamedata/excel/character_table.json'
53-
const UNIEQUIP_TABLE_JSON_URL =
53+
const UNIEQUIP_TABLE_JSON_URL_CN =
5454
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData/master/zh_CN/gamedata/excel/uniequip_table.json'
55-
55+
const CHARACTER_TABLE_JSON_URL_EN =
56+
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData_YoStar/main/en_US/gamedata/excel/character_table.json'
57+
const UNIEQUIP_TABLE_JSON_URL_EN =
58+
'https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData_YoStar/main/en_US/gamedata/excel/uniequip_table.json'
5659
const CHARACTER_BLOCKLIST = [
5760
'char_512_aprot', // 暮落(集成战略):It's just not gonna be there.
5861
'token_10012_rosmon_shield', // 迷迭香的战术装备:It's just not gonna be there.
@@ -74,37 +77,55 @@ async function json(url: string) {
7477
}
7578

7679
export async function getOperators() {
77-
const [charTable, uniequipTable] = await Promise.all([
78-
json(CHARACTER_TABLE_JSON_URL),
79-
json(UNIEQUIP_TABLE_JSON_URL),
80-
])
80+
const [charTableCN, uniequipTableCN, charTableEN, uniequipTableEN] =
81+
await Promise.all([
82+
json(CHARACTER_TABLE_JSON_URL_CN),
83+
json(UNIEQUIP_TABLE_JSON_URL_CN),
84+
json(CHARACTER_TABLE_JSON_URL_EN),
85+
json(UNIEQUIP_TABLE_JSON_URL_EN),
86+
])
8187

82-
const { subProfDict } = uniequipTable
88+
const { subProfDict: subProfDictCN } = uniequipTableCN
89+
const { subProfDict: subProfDictEN } = uniequipTableEN
8390

84-
const opIds = Object.keys(charTable)
91+
const opIds = Object.keys(charTableCN)
8592
const professions: Professions = []
8693
const result = uniqBy(
8794
opIds.flatMap((id) => {
88-
const op = charTable[id]
95+
const op = charTableCN[id]
96+
const enName = charTableEN[id]?.name || op.appellation || op.name
97+
8998
if (['TRAP'].includes(op.profession)) return []
9099

91100
if (!['TOKEN'].includes(op.profession)) {
92101
const prof = professions.find((p) => p.id === op.profession)
93102
if (!prof) {
103+
const enSubProfName =
104+
subProfDictEN?.[op.subProfessionId]?.subProfessionName ||
105+
subProfDictCN[op.subProfessionId].subProfessionName
106+
94107
professions.push({
95108
id: op.profession,
96109
name: PROFESSION_NAMES[op.profession],
110+
name_en:
111+
op.profession.charAt(0) + op.profession.slice(1).toLowerCase(),
97112
sub: [
98113
{
99114
id: op.subProfessionId,
100-
name: subProfDict[op.subProfessionId].subProfessionName,
115+
name: subProfDictCN[op.subProfessionId].subProfessionName,
116+
name_en: enSubProfName,
101117
},
102118
],
103119
})
104120
} else if (!prof.sub.find((p) => p.id === op.subProfessionId)) {
121+
const enSubProfName =
122+
subProfDictEN?.[op.subProfessionId]?.subProfessionName ||
123+
subProfDictCN[op.subProfessionId].subProfessionName
124+
105125
prof.sub.push({
106126
id: op.subProfessionId,
107-
name: subProfDict[op.subProfessionId].subProfessionName,
127+
name: subProfDictCN[op.subProfessionId].subProfessionName,
128+
name_en: enSubProfName,
108129
})
109130
}
110131
}
@@ -113,6 +134,7 @@ export async function getOperators() {
113134
id: id,
114135
prof: op.profession,
115136
subProf: op.subProfessionId,
137+
name_en: enName,
116138
...transformOperatorName(op.name),
117139
rarity:
118140
op.subProfessionId === 'notchar1'

src/components/OperationCard.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Button, Card, Elevation, H4, H5, Icon, Tag } from '@blueprintjs/core'
22
import { Tooltip2 } from '@blueprintjs/popover2'
33

44
import clsx from 'clsx'
5+
import { useAtomValue } from 'jotai'
56
import { CopilotInfoStatusEnum } from 'maa-copilot-client'
67
import { copyShortCode, handleLazyDownloadJSON } from 'services/operation'
78

@@ -11,8 +12,9 @@ import { OperationRating } from 'components/viewer/OperationRating'
1112
import { OpDifficulty, Operation } from 'models/operation'
1213

1314
import { useLevels } from '../apis/level'
14-
import { useTranslation } from '../i18n/i18n'
15+
import { languageAtom, useTranslation } from '../i18n/i18n'
1516
import { createCustomLevel, findLevelByStageName } from '../models/level'
17+
import { getLocalizedOperatorName } from '../models/operator'
1618
import { Paragraphs } from './Paragraphs'
1719
import { ReLinkDiv } from './ReLinkDiv'
1820
import { UserName } from './UserName'
@@ -245,13 +247,14 @@ export const OperationCard = ({ operation }: { operation: Operation }) => {
245247

246248
const OperatorTags = ({ operation }: { operation: Operation }) => {
247249
const t = useTranslation()
250+
const language = useAtomValue(languageAtom)
248251
const { opers, groups } = operation.parsedContent
249252

250253
return opers?.length || groups?.length ? (
251254
<div>
252255
{opers?.map(({ name, skill }, index) => (
253256
<Tag key={index} className="mr-2 last:mr-0 mb-1 last:mb-0">
254-
{`${name} ${skill ?? 1}`}
257+
{`${getLocalizedOperatorName(name, language)} ${skill ?? 1}`}
255258
</Tag>
256259
))}
257260
{groups?.map(({ name, opers }, index) => (
@@ -261,7 +264,10 @@ const OperatorTags = ({ operation }: { operation: Operation }) => {
261264
placement="top"
262265
content={
263266
opers
264-
?.map(({ name, skill }) => `${name} ${skill ?? 1}`)
267+
?.map(
268+
({ name, skill }) =>
269+
`${getLocalizedOperatorName(name, language)} ${skill ?? 1}`,
270+
)
265271
.join(', ') || t.components.OperationCard.no_operators
266272
}
267273
>

src/components/OperatorFilter.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import {
99
} from '@blueprintjs/core'
1010

1111
import clsx from 'clsx'
12-
import { getDefaultStore, useAtom } from 'jotai'
12+
import { getDefaultStore, useAtom, useAtomValue } from 'jotai'
1313
import { compact } from 'lodash-es'
1414
import { FC, useEffect, useMemo, useState } from 'react'
1515

16-
import { useTranslation } from '../i18n/i18n'
16+
import { languageAtom, useTranslation } from '../i18n/i18n'
1717
import { OPERATORS } from '../models/operator'
1818
import {
1919
DEFAULT_OPERATOR_FILTER,
@@ -53,6 +53,7 @@ export const OperatorFilter: FC<OperatorFilterProps> = ({
5353
onChange,
5454
}) => {
5555
const t = useTranslation()
56+
const language = useAtomValue(languageAtom)
5657
const [savedFilter, setSavedFilter] = useAtom(operatorFilterAtom)
5758
const [dialogOpen, setDialogOpen] = useState(false)
5859
const [editingFilter, setEditingFilter] = useState<typeof savedFilter>(filter)
@@ -137,27 +138,29 @@ export const OperatorFilter: FC<OperatorFilterProps> = ({
137138
!filter.enabled && 'opacity-30',
138139
)}
139140
>
140-
{includedOperators.map(({ id, name, rarity }) => (
141+
{includedOperators.map(({ id, name, name_en, rarity }) => (
141142
<Tag minimal key={id} className="py-0 pl-0" intent="primary">
142143
<div className="flex items-center gap-1 text-sm">
143144
<OperatorAvatar
144145
className="w-8 h-8"
145146
id={id}
146147
rarity={rarity}
147148
/>
148-
&nbsp;{name}&nbsp;
149+
&nbsp;{language === 'en' ? name_en : name}
150+
&nbsp;
149151
</div>
150152
</Tag>
151153
))}
152-
{excludedOperators.map(({ id, name, rarity }) => (
154+
{excludedOperators.map(({ id, name, name_en, rarity }) => (
153155
<Tag minimal key={id} className="py-0 pl-0" intent="danger">
154156
<div className="flex items-center gap-1 text-sm line-through">
155157
<OperatorAvatar
156158
className="w-8 h-8"
157159
id={id}
158160
rarity={rarity}
159161
/>
160-
&nbsp;{name}&nbsp; {/* 两边加空格让删除线更显眼一些 */}
162+
&nbsp;{language === 'en' ? name_en : name}
163+
&nbsp; {/* 两边加空格让删除线更显眼一些 */}
161164
</div>
162165
</Tag>
163166
))}

src/components/OperatorSelect.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { MultiSelect2 } from '@blueprintjs/select'
44

55
import clsx from 'clsx'
66
import Fuse from 'fuse.js'
7+
import { useAtomValue } from 'jotai'
78
import { compact } from 'lodash-es'
89
import { FC, useMemo } from 'react'
910

10-
import { useTranslation } from '../i18n/i18n'
11+
import { languageAtom, useTranslation } from '../i18n/i18n'
1112
import { OPERATORS } from '../models/operator'
1213
import { useDebouncedQuery } from '../utils/useDebouncedQuery'
1314
import { OperatorAvatar } from './editor/operator/EditorOperator'
@@ -26,13 +27,14 @@ export const OperatorSelect: FC<OperatorSelectProps> = ({
2627
onChange,
2728
}) => {
2829
const t = useTranslation()
30+
const language = useAtomValue(languageAtom)
2931
const { query, trimmedDebouncedQuery, updateQuery, onOptionMouseDown } =
3032
useDebouncedQuery()
3133

3234
const fuse = useMemo(
3335
() =>
3436
new Fuse(OPERATORS, {
35-
keys: ['name', 'alias', 'alt_name'],
37+
keys: ['name', 'name_en', 'alias', 'alt_name'],
3638
threshold: 0.3,
3739
}),
3840
[],
@@ -82,7 +84,7 @@ export const OperatorSelect: FC<OperatorSelectProps> = ({
8284
id={item.id}
8385
rarity={item.rarity}
8486
/>
85-
{item.name}
87+
{language === 'en' ? item.name_en : item.name}
8688
</div>
8789
}
8890
onClick={handleClick}
@@ -129,7 +131,7 @@ export const OperatorSelect: FC<OperatorSelectProps> = ({
129131
id={item.id}
130132
rarity={item.rarity}
131133
/>
132-
{item.name}
134+
{language === 'en' ? item.name_en : item.name}
133135
</div>
134136
)}
135137
popoverProps={{

src/components/editor/operator/EditorOperator.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { Icon, IconSize, MenuItem } from '@blueprintjs/core'
22

33
import clsx from 'clsx'
44
import Fuse from 'fuse.js'
5+
import { useAtomValue } from 'jotai'
56
import { useMemo } from 'react'
67
import { FieldValues, useController } from 'react-hook-form'
78

89
import { EditorFieldProps } from 'components/editor/EditorFieldProps'
910

10-
import { useTranslation } from '../../../i18n/i18n'
11+
import { languageAtom, useTranslation } from '../../../i18n/i18n'
1112
import { CopilotDocV1 } from '../../../models/copilot.schema'
1213
import { OPERATORS } from '../../../models/operator'
1314
import { Suggest } from '../../Suggest'
@@ -27,6 +28,7 @@ const createArbitraryOperator = (name: string): OperatorInfo => ({
2728
alias: '',
2829
alt_name: '',
2930
subProf: '',
31+
name_en: '',
3032
prof: '',
3133
rarity: 0,
3234
})
@@ -43,6 +45,7 @@ export const EditorOperatorName = <T extends FieldValues>({
4345
operators?: CopilotDocV1.Operator[]
4446
}) => {
4547
const t = useTranslation()
48+
const language = useAtomValue(languageAtom)
4649

4750
const entityName = useMemo(
4851
() =>
@@ -90,7 +93,7 @@ export const EditorOperatorName = <T extends FieldValues>({
9093
const fuse = useMemo(
9194
() =>
9295
new Fuse(items, {
93-
keys: ['name', 'alias', 'alt_name'],
96+
keys: ['name', 'name_en', 'alias', 'alt_name'],
9497
threshold: 0.3,
9598
}),
9699
[items],
@@ -107,7 +110,11 @@ export const EditorOperatorName = <T extends FieldValues>({
107110
itemRenderer={(item, { handleClick, handleFocus, modifiers }) => (
108111
<MenuItem
109112
key={'id' in item ? item.id : item.name}
110-
text={item.name}
113+
text={
114+
isOperator(item) && language === 'en' && item.name_en
115+
? item.name_en
116+
: item.name
117+
}
111118
icon={
112119
isOperator(item) ? (
113120
<OperatorAvatar id={item.id} size="small" />
@@ -123,7 +130,11 @@ export const EditorOperatorName = <T extends FieldValues>({
123130
)}
124131
onItemSelect={(item) => onChange(item.name)}
125132
selectedItem={createArbitraryOperator((value || '') as string)}
126-
inputValueRenderer={(item) => item.name}
133+
inputValueRenderer={(item) =>
134+
isOperator(item) && language === 'en' && item.name_en
135+
? item.name_en
136+
: item.name
137+
}
127138
createNewItemFromQuery={(query) => createArbitraryOperator(query)}
128139
createNewItemRenderer={(query, active, handleClick) => (
129140
<MenuItem

src/components/editor/operator/EditorOperatorItem.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { Card, Elevation, Icon } from '@blueprintjs/core'
22

33
import clsx from 'clsx'
4+
import { useAtomValue } from 'jotai'
45

56
import type { CopilotDocV1 } from 'models/copilot.schema'
67

7-
import { useTranslation } from '../../../i18n/i18n'
8-
import { OPERATORS, getSkillUsageTitle } from '../../../models/operator'
8+
import { languageAtom, useTranslation } from '../../../i18n/i18n'
9+
import {
10+
OPERATORS,
11+
getLocalizedOperatorName,
12+
getSkillUsageTitle,
13+
} from '../../../models/operator'
914
import { SortableItemProps } from '../../dnd'
1015
import { CardDeleteOption, CardEditOption } from '../CardOptions'
1116
import { OperatorAvatar } from './EditorOperator'
@@ -27,7 +32,7 @@ export const EditorOperatorItem = ({
2732
listeners,
2833
}: EditorOperatorItemProps) => {
2934
const t = useTranslation()
30-
35+
const language = useAtomValue(languageAtom)
3136
const id = OPERATORS.find(({ name }) => name === operator.name)?.id
3237
const skillUsage = getSkillUsageTitle(
3338
operator.skillUsage as CopilotDocV1.SkillUsageType,
@@ -52,7 +57,9 @@ export const EditorOperatorItem = ({
5257
/>
5358
<OperatorAvatar id={id} size="large" />
5459
<div className="ml-4 flex-grow">
55-
<h3 className="font-bold leading-none mb-1">{operator.name}</h3>
60+
<h3 className="font-bold leading-none mb-1">
61+
{getLocalizedOperatorName(operator.name, language)}
62+
</h3>
5663
<div className="text-gray-400 text-xs">
5764
{t.components.editor.operator.EditorOperatorItem.skill_number({
5865
count: operator.skill,

src/components/editor/operator/sheet/SheetOperatorSkillAbout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export const SkillAboutTrigger = ({
115115
const SkillAboutTrigger = (
116116
<div
117117
className={clsx(
118-
'flex mt-3 text-gray-500 items-center text-xs',
118+
'flex mt-1 text-gray-500 items-center text-xs',
119119
operator && 'hover:text-black',
120120
)}
121121
>

src/components/editor/operator/sheet/sheetGroup/OperatorInGroupItem.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Card } from '@blueprintjs/core'
22

33
import { FC } from 'react'
44

5+
import { useLocalizedOperatorName } from 'models/operator'
6+
57
import { OperatorAvatar } from '../../EditorOperator'
68
import { Operator } from '../../EditorSheet'
79
import { SkillAboutTrigger } from '../SheetOperatorSkillAbout'
@@ -20,7 +22,7 @@ export const OperatorInGroupItem: FC<OperatorInGroupItemProp> = ({
2022
<div className="flex items-center">
2123
<OperatorAvatar name={name} size="large" />
2224
<p className="font-bold leading-none text-center truncate ml-2 mr-auto">
23-
{name}
25+
{useLocalizedOperatorName(name)}
2426
</p>
2527
</div>
2628
{!!onOperatorSkillChange && (

0 commit comments

Comments
 (0)