feat: add French localization support for character attributes and improve character display logic
All checks were successful
Build Docker Image / build (push) Successful in 1m18s

- Added optional French names, affiliations, origins, and epithets to character records.
- Updated character import logic to handle new French fields.
- Enhanced character search and display components to show French names and epithets based on selected language.
- Modified database schema to include French fields for characters.
- Improved error handling in daily character setup to check for existing characters.
- Refactored components to utilize helper functions for displaying names and attributes based on language.
This commit is contained in:
2026-03-15 22:00:19 +01:00
parent bd121b7d85
commit 997b2f1781
15 changed files with 655 additions and 236 deletions

View File

@@ -1,144 +1,255 @@
[
"aladdin_aladdin",
"alvida_alvida",
"aramaki_aramaki",
"arlong_arlong",
"ashura_doji_ashura_doji",
"baby_5_baby_5",
"baggy_baggy",
"bartholomew_kuma_bartholomew_kuma",
"bartolomeo_bartolomeo",
"basil_hawkins_basil_hawkins",
"batman_batman",
"bellamy_bellamy",
"belo_betty_belo_betty",
"ben_beckman_ben_beckman",
"bentham_bentham",
"bepo_bepo",
"black_maria_black_maria",
"boa_hancock_boa_hancock",
"boa_marigold_boa_marigold",
"boa_sandersonia_boa_sandersonia",
"borsalino_borsalino",
"brogy_brogy",
"brook_brook",
"camie_camie",
"capone_bege_capone_bege",
"caribou_caribou",
"carrot_carrot",
"catarina_devon_catarina_devon",
"cavendish_cavendish",
"cesar_clown_cesar_clown",
"chinjao_chinjao",
"coby_coby",
"corazon_corazon",
"crocodile_crocodile",
"crocus_crocus",
"curly_dadan_curly_dadan",
"dalton_dalton",
"daz_bones_daz_bones",
"denjiro_denjiro",
"diamante_diamante",
"doc_q_doc_q",
"don_quichotte_doflamingo_don_quichotte_doflamingo",
"don_quichotte_rossinante_don_quichotte_rossinante",
"dorry_dorry",
"dracule_mihawk_dracule_mihawk",
"duval_duval",
"edward_newgate_edward_newgate",
"edward_weevil_edward_weevil",
"emporio_ivankov_emporio_ivankov",
"enel_enel",
"eustass_kid_eustass_kid",
"fisher_tiger_fisher_tiger",
"foxy_foxy",
"franky_franky",
"fujitora_fujitora",
"gan_forr_gan_forr",
"gecko_moria_gecko_moria",
"gin_gin",
"gol_d_roger_gol_d_roger",
"haguar_d_sauro_haguar_d_sauro",
"hajrudin_hajrudin",
"hannyabal_hannyabal",
"hatchan_hatchan",
"hina_hina",
"hody_jones_hody_jones",
"hyogoro_hyogoro",
"iceburg_iceburg",
"imu_imu",
"inazuma_inazuma",
"inuarashi_inuarashi",
"issho_issho",
"izo_izo",
"jabra_jabra",
"jack_jack",
"jesus_burgess_jesus_burgess",
"jewelry_bonney_jewelry_bonney",
"jinbei_jinbei",
"joy_boy_joy_boy",
"kaidou_kaidou",
"kaku_kaku",
"kalgara_kalgara",
"kalifa_kalifa",
"karasu_karasu",
"karoo_karoo",
"kawamatsu_kawamatsu",
"kaya_kaya",
"killer_killer",
"kinemon_kinemon",
"koala_koala",
"koby_koby",
"kong_kong",
"kozuki_hiyori_kozuki_hiyori",
"kozuki_momonosuke_kozuki_momonosuke",
"kozuki_oden_kozuki_oden",
"krieg_krieg",
"kureha_kureha",
"kuro_kuro",
"kurozumi_orochi_kurozumi_orochi",
"kuzan_kuzan",
"kyros_kyros",
"laboon_laboon",
"laffitte_laffitte",
"lao_g_lao_g",
"leo_leo",
"lindbergh_lindbergh",
"loki_loki",
"lucky_roux_lucky_roux",
"magellan_magellan",
"makino_makino",
"marco_marco",
"marshall_d_teach_marshall_d_teach",
"monkey_d_dragon_monkey_d_dragon",
"monkey_d_garp_monkey_d_garp",
"monkey_d_luffy_monkey_d_luffy",
"montblanc_norland_montblanc_norland",
"morgans_morgans",
"morley_morley",
"mr_3_mr_3",
"nami_nami",
"nefertari_cobra_nefertari_cobra",
"nefertari_vivi_nefertari_vivi",
"nekomamushi_nekomamushi",
"neptune_neptune",
"nico_robin_nico_robin",
"oars_oars",
"otohime_otohime",
"page_one_page_one",
"pandaman_pandaman",
"pekoms_pekoms",
"pell_pell",
"perona_perona",
"pica_pica",
"portgas_d_ace_portgas_d_ace",
"queen_queen",
"raizo_raizo",
"rebecca_rebecca",
"rob_lucci_rob_lucci",
"rocks_d_xebec_rocks_d_xebec",
"roronoa_zoro_roronoa_zoro",
"sabo_sabo",
"vegapunk_vegapunk",
"yamato_yamato"
"absalom_absalom",
"king_king",
"alvida_alvida",
"aramaki_aramaki",
"arlong_arlong",
"ashura_doji_ashura_doji",
"vegapunk/atlas_atlas",
"avalo_pizarro_avalo_pizarro",
"baby_5_baby_5",
"buggy_buggy",
"bartholomew_kuma_bartholomew_kuma",
"bartolomeo_bartolomeo",
"basil_hawkins_basil_hawkins",
"bell-mère_bell-mère",
"bellamy_bellamy",
"belo_betty_belo_betty",
"benn_beckman_ben_beckman",
"bentham_bentham",
"bepo_bepo",
"black_maria_black_maria",
"blueno_blueno",
"boa_hancock_boa_hancock",
"boa_marigold_boa_marigold",
"boa_sandersonia_boa_sandersonia",
"borsalino_borsalino",
"brogy_brogy",
"brook_brook",
"buckingham_stussy_buckingham_stussy",
"buffalo_buffalo",
"camie_camie",
"capone_bege_capone_bege",
"carmel_carmel",
"caribou_caribou",
"carrot_carrot",
"catarina_devon_catarina_devon",
"cavendish_cavendish",
"caesar_clown_caesar_clown",
"charlotte_brûlée_charlotte_brûlée",
"charlotte_cracker_charlotte_cracker",
"charlotte_katakuri_charlotte_katakuri",
"charlotte_linlin_charlotte_linlin",
"charlotte_mont-d'or_charlotte_mont-d'or",
"charlotte_oven_charlotte_oven",
"charlotte_perospero_charlotte_perospero",
"charlotte_pudding_charlotte_pudding",
"charlotte_smoothie_charlotte_smoothie",
"chinjao_chinjao",
"clou_d_clover_clou_d_clover",
"crocodile_crocodile",
"crocus_crocus",
"curly_dadan_curly_dadan",
"dalton_dalton",
"daz_bonez_daz_bonez",
"denjiro_denjiro",
"diamante_diamante",
"doc_q_doc_q",
"donquixote_doflamingo_donquixote_doflamingo",
"donquixote_rosinante_donquixote_rosinante",
"dorry_dorry",
"dracule_mihawk_dracule_mihawk",
"vegapunk/edison_edison",
"edward_newgate_edward_newgate",
"edward_weevil_edward_weevil",
"emporio_ivankov_emporio_ivankov",
"enel_enel",
"eustass_kid_eustass_kid",
"fisher_tiger_fisher_tiger",
"foxy_foxy",
"franky_franky",
"fukaboshi_fukaboshi",
"fukurou_fukurou",
"galdino_galdino",
"gan_fall_gan_fall",
"gecko_moria_gecko_moria",
"gem_gem",
"genzo_genzo",
"gin_gin",
"ginny_ginny",
"gol_d_roger_gol_d_roger",
"guernika_guernika",
"hack_hack",
"jaguar_d_saul_jaguar_d_saul",
"hajrudin_hajrudin",
"hannyabal_hannyabal",
"harald_harald",
"haredas_haredas",
"heracles_heracles",
"helmeppo_helmeppo",
"hibari_hibari",
"hiriluk_hiriluk",
"hina_hina",
"hody_jones_hody_jones",
"hogback_hogback",
"hyougoro_hyougoro",
"iceburg_iceburg",
"igaram_igaram",
"imu_imu",
"inazuma_inazuma",
"inuarashi_inuarashi",
"issho_issho",
"izou_izou",
"jabra_jabra",
"jack_jack",
"jango_jango",
"jesus_burgess_jesus_burgess",
"jewelry_bonney_jewelry_bonney",
"jinbe_jinbe",
"giolla_giolla",
"joy_boy_joy_boy",
"jozu_jozu",
"kaidou_kaidou",
"kaku_kaku",
"kalgara_kalgara",
"kalifa_kalifa",
"karasu_karasu",
"karoo_karoo",
"kawamatsu_kawamatsu",
"kaya_kaya",
"kelly_funk_kelly_funk",
"kikunojo_kikunojo",
"killer_killer",
"kin'emon_kin'emon",
"koala_koala",
"koby_koby",
"kokoro_kokoro",
"kouzuki_hiyori_kouzuki_hiyori",
"kouzuki_momonosuke_kouzuki_momonosuke",
"kouzuki_oden_kouzuki_oden",
"kouzuki_sukiyaki_kouzuki_sukiyaki",
"kouzuki_toki_kouzuki_toki",
"krieg_krieg",
"kumadori_kumadori",
"kureha_kureha",
"kuro_kuro",
"kurozumi_kanjuro_kurozumi_kanjuro",
"kurozumi_orochi_kurozumi_orochi",
"kurozumi_tama_kurozumi_tama",
"kuzan_kuzan",
"kyros_kyros",
"laboon_laboon",
"laffitte_laffitte",
"lao_g_lao_g",
"leo_leo",
"vegapunk/lilith_lilith",
"lindbergh_lindbergh",
"loki_loki",
"lucky_roux_lucky_roux",
"magellan_magellan",
"makino_makino",
"mansherry_mansherry",
"marco_marco",
"marshall_d_teach_marshall_d_teach",
"merry_merry",
"momoo_momoo",
"mocha_mocha",
"monet_monet",
"monkey_d_dragon_monkey_d_dragon",
"monkey_d_garp_monkey_d_garp",
"monkey_d_luffy_monkey_d_luffy",
"mont_blanc_cricket_mont_blanc_cricket",
"mont_blanc_noland_mont_blanc_noland",
"morgans_morgans",
"morgan_morgan",
"morley_morley",
"nami_nami",
"nefertari_cobra_nefertari_cobra",
"nefertari_vivi_nefertari_vivi",
"nekomamushi_nekomamushi",
"neptune_neptune",
"nico_olvia_nico_olvia",
"nico_robin_nico_robin",
"nojiko_nojiko",
"hatchan_hatchan",
"otohime_otohime",
"oars_oars",
"page_one_page_one",
"pandaman_pandaman",
"paulie_paulie",
"pedro_pedro",
"pekoms_pekoms",
"pell_pell",
"perona_perona",
"pica_pica",
"portgas_d_ace_portgas_d_ace",
"vegapunk/pythagoras_pythagoras",
"queen_queen",
"raizo_raizo",
"rebecca_rebecca",
"riku_doldo_iii_riku_doldo_iii",
"rob_lucci_rob_lucci",
"rocks_d_xebec_rocks_d_xebec",
"roronoa_zoro_roronoa_zoro",
"s-bear_s-bear",
"s-hawk_s-hawk",
"s-snake_s-snake",
"sabo_sabo",
"sadi_sadi",
"donquixote_mjosgard_donquixote_mjosgard",
"rimoshifu_killingham_rimoshifu_killingham",
"manmayer_gunko_manmayer_gunko",
"shepherd_sommers_shepherd_sommers",
"sakazuki_sakazuki",
"sanjuan_wolf_sanjuan_wolf",
"sasaki_sasaki",
"scratchmen_apoo_scratchmen_apoo",
"sengoku_sengoku",
"senor_pink_senor_pink",
"sentomaru_sentomaru",
"vegapunk/shaka_shaka",
"shakuyaku_shakuyaku",
"shanks_shanks",
"shiryu_shiryu",
"shimotsuki_kuina_shimotsuki_kuina",
"shimotsuki_yasuie_shimotsuki_yasuie",
"shinobu_shinobu",
"shirahoshi_shirahoshi",
"silvers_rayleigh_silvers_rayleigh",
"smoker_smoker",
"spandam_spandam",
"speed_speed",
"stussy_stussy",
"sugar_sugar",
"tamago_tamago",
"tashigi_tashigi",
"toko_toko",
"tom_tom",
"tony_tony_chopper_tony_tony_chopper",
"trafalgar_d_water_law_trafalgar_d_water_law",
"trebol_trebol",
"tsuru_tsuru",
"ulti_ulti",
"urouge_urouge",
"usopp_usopp",
"uta_uta",
"van_augur_van_augur",
"vander_decken_ix_vander_decken_ix",
"vegapunk_vegapunk",
"vergo_vergo",
"vinsmoke_ichiji_vinsmoke_ichiji",
"vinsmoke_judge_vinsmoke_judge",
"vinsmoke_niji_vinsmoke_niji",
"vinsmoke_reiju_vinsmoke_reiju",
"sanji_sanji",
"vinsmoke_yonji_vinsmoke_yonji",
"viola_viola",
"wadatsumi_wadatsumi",
"wapol_wapol",
"wyper_wyper",
"x_drake_x_drake",
"yamato_yamato",
"yasopp_yasopp",
"vegapunk/york_york",
"zeff_zeff"
]

View File

@@ -9,6 +9,7 @@ type Status = 'Alive' | 'Dead' | 'Unknown';
type ArcRecord = {
id: string;
name: string;
frName?: string | null;
startChapter: number;
endChapter?: number | null;
url?: string | null;
@@ -170,6 +171,7 @@ async function importFromJson(): Promise<void> {
.values({
id: item.id,
name: item.name,
frName: toNullable(item.frName),
startChapter: item.startChapter,
endChapter: toNullable(item.endChapter),
url: toNullable(item.url)
@@ -178,6 +180,7 @@ async function importFromJson(): Promise<void> {
target: arc.id,
set: {
name: item.name,
frName: toNullable(item.frName),
startChapter: item.startChapter,
endChapter: toNullable(item.endChapter),
url: toNullable(item.url)

View File

@@ -1,6 +1,6 @@
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
import { eq } from 'drizzle-orm';
import { eq, inArray } from 'drizzle-orm';
import fs from 'fs';
import { character, characterHistory } from '../src/lib/server/db/schema';
@@ -24,13 +24,14 @@ function getErrorMessage(error: unknown): string {
async function setDailyCharacters(): Promise<void> {
try {
const dailyCharacterIds = readJsonFile('./scripts/daily-characters.json');
const dailyCharacterIdsRaw = readJsonFile('./scripts/daily-characters.json');
if (!dailyCharacterIds || dailyCharacterIds.length === 0) {
console.error('No daily characters found in daily-characters.json');
process.exit(1);
if (!dailyCharacterIdsRaw || dailyCharacterIdsRaw.length === 0) {
throw new Error('No daily characters found in daily-characters.json');
}
const dailyCharacterIds = dailyCharacterIdsRaw;
console.log(`\n=== Setting Daily Mode Characters ===\n`);
console.log(`Found ${dailyCharacterIds.length} characters to set as daily\n`);
@@ -45,16 +46,36 @@ async function setDailyCharacters(): Promise<void> {
let successCount = 0;
let errorCount = 0;
const existingCharacters = await db
.select({ id: character.id })
.from(character)
.where(inArray(character.id, dailyCharacterIds));
const existingIdSet = new Set(existingCharacters.map((c) => c.id));
const missingIds = dailyCharacterIds.filter((id) => !existingIdSet.has(id));
if (missingIds.length > 0) {
errorCount += missingIds.length;
console.error(`${missingIds.length} character ID(s) were not found in database:`);
for (const missingId of missingIds) {
console.error(` - ${missingId}`);
}
console.error('');
}
for (let i = 0; i < dailyCharacterIds.length; i++) {
const charId = dailyCharacterIds[i];
if (!existingIdSet.has(charId)) {
continue;
}
try {
const result = await db
await db
.update(character)
.set({ isInDailyMode: true })
.where(eq(character.id, charId));
successCount++;
process.stdout.write(`\rUpdated: ${successCount}/${dailyCharacterIds.length}`);
} catch (error) {
errorCount++;
console.error(`\n✗ Error updating character ${i + 1}:`);