This commit is contained in:
Zylan
2025-03-31 03:49:05 +08:00
parent 34dc46a635
commit cbb6450e27
5 changed files with 568 additions and 12 deletions

View File

@@ -23,6 +23,7 @@ function ShareCardWrapper() {
const workDaysPerYear = searchParams.get('workDaysPerYear') || '250';
const countryCode = searchParams.get('countryCode') || 'CN';
const countryName = searchParams.get('countryName') || '中国';
const currencySymbol = searchParams.get('currencySymbol') || '¥';
// 额外参数 - 详细工作信息
const workDaysPerWeek = searchParams.get('workDaysPerWeek') || '5';
@@ -63,6 +64,7 @@ function ShareCardWrapper() {
workDaysPerYear={workDaysPerYear}
countryCode={countryCode}
countryName={countryName}
currencySymbol={currencySymbol}
// 详细工作信息
workDaysPerWeek={workDaysPerWeek}

View File

@@ -3,7 +3,7 @@
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
// 定义语言类型
export type Language = 'zh' | 'en';
export type Language = 'zh' | 'en' | 'ja';
// 创建上下文接口
interface LanguageContextType {
@@ -27,7 +27,6 @@ const translations: Record<Language, Record<string, string>> = {
zh: {
// 标题和导航
'title': '这b班上得值不值·测算版',
'version': 'v5.1.0',
'github': 'GitHub',
'email': 'Email',
'xiaohongshu': '小红书',
@@ -252,8 +251,7 @@ const translations: Record<Language, Record<string, string>> = {
},
en: {
// Title and navigation
'title': 'Is My Damn Job Worth It?',
'version': 'v5.1.0',
'title': 'Is My Job Worth the Grind?',
'github': 'GitHub',
'email': 'Email',
'xiaohongshu': 'Rednote',
@@ -475,6 +473,231 @@ const translations: Record<Language, Record<string, string>> = {
'rating_excellent': 'Excellent',
'rating_perfect': 'Outstanding',
'share_country': 'Work Country/Region',
},
ja: {
// タイトルとナビゲーション
'title': 'この仕事、割に合ってる?ブラック度診断',
'github': 'GitHub',
'email': 'Email',
'xiaohongshu': '小紅書',
'redirect_notice': '自動的にリダイレクトされました',
'visits': 'アクセス数',
'visitors': '訪問者数',
'star_request': '役に立ったら、⭐スターを付けてください!',
// フォームラベル
'annual_salary_cny': '年収(元)',
'annual_salary_foreign': '年収(現地通貨)',
'salary_placeholder_cny': '税引前の年収',
'salary_placeholder_foreign': '現地通貨で入力',
'non_china_salary': '中国以外の給与',
'ppp_factor': '購買力平価(PPP)換算係数',
'ppp_tooltip': 'PPP換算係数は各国の通貨の購買力を標準化する指標です。例えば、中国の4.19は、米国の1ドルが中国の4.19元と同等の購買力を持つことを意味します。',
'ppp_placeholder': 'PPP換算係数を入力',
'ppp_common_regions': '主な地域:中国:4.19、日本:102.59、米国:1.00、シンガポール:0.84',
'view_more': 'もっと見る',
'country_selection': '勤務国・地域',
'selected_ppp': '現在のPPP値',
'work_days_per_week': '週の勤務日数/日',
'wfh_days_per_week': 'リモートワーク日数/週',
'wfh_tooltip': 'リモートワークとは在宅勤務のことです。週の勤務日のうち、何日自宅で仕事をするかを入力してください。',
'annual_leave': '年次有給休暇/日',
'public_holidays': '祝日/日',
'paid_sick_leave': '有給病気休暇/日',
'work_hours': '勤務時間/時間',
'work_hours_tooltip': '勤務時間:始業から終業までの総時間(昼食、休憩、残業を含む、通勤時間は除く)',
'commute_hours': '通勤時間/時間',
'commute_tooltip': '通勤時間は自宅と職場の往復にかかる総時間です。',
'rest_time': '休憩&サボり時間',
// 環境係数
'job_stability': '雇用形態',
'job_private': '一般企業(更新あり)',
'job_foreign': '外資系企業(更新あり)',
'job_state': '大企業・終身雇用',
'job_government': '公務員・準公務員',
'work_environment': '職場環境',
'env_remote': '僻地の工場・現場・屋外',
'env_factory': '工場・現場・屋外',
'env_normal': '一般的なオフィス',
'env_cbd': '都心の高級オフィス',
'city_factor': '勤務地(生活コストによる)',
'city_tier1': '東京23区・大阪市中心部',
'city_newtier1': '横浜・名古屋・福岡市中心部',
'city_tier2': '県庁所在地・中核市',
'city_tier3': '地方都市',
'city_tier4': '小都市',
'city_county': '郡部',
'city_town': '町村部',
'hometown': '地元で働いていますか',
'not_hometown': '地元ではない',
'is_hometown': '地元である',
'leadership': '上司・経営者',
'leader_bad': '嫌われている',
'leader_strict': '厳しい管理',
'leader_normal': '普通の関係',
'leader_good': '理解がある',
'leader_favorite': '気に入られている',
'teamwork': '職場の人間関係',
'team_bad': '最悪な環境',
'team_normal': '事務的な関係',
'team_good': '協力的な環境',
'team_excellent': '仲の良い職場',
'shuttle': '送迎バス(加点項目)',
'shuttle_none': 'なし',
'shuttle_inconvenient': '不便なバス',
'shuttle_convenient': '便利なバス',
'shuttle_direct': '直行便あり',
'canteen': '社員食堂(加点項目)',
'canteen_none': 'なし/まずい',
'canteen_average': '普通',
'canteen_good': '美味しい',
'canteen_excellent': '非常に美味しい',
// 教育と経験
'education_level': '学歴',
'degree_type': '学位タイプ',
'below_bachelor': '短大・専門学校以下',
'bachelor': '学士(大学卒)',
'masters': '修士',
'phd': '博士',
'school_type': '大学タイプ',
'school_second_tier': '一般大学',
'school_first_tier_bachelor': 'MARCH・関関同立・QS200位',
'school_elite_bachelor': '東大・京大・早慶・QS50位',
'school_first_tier_higher': '国公立大学院・QS100位',
'school_elite_higher': '東大・京大大学院・QS30位',
'bachelor_background': '学部背景',
'work_years': '勤務年数',
'fresh_graduate': '新卒',
'years_1_3': '1-3年',
'years_3_5': '3-5年',
'years_5_8': '5-8年',
'years_8_10': '8-10年',
'years_10_12': '10-12年',
'years_above_12': '12年以上',
// 結果
'working_days_per_year': '年間勤務日数',
'days_unit': '日',
'average_daily_salary': '1日あたりの給与',
'job_value': '仕事の価値評価',
'view_report': '私の仕事診断レポートを見る',
// ShareCardコンポーネント
'share_back_to_calculator': '計算機に戻る',
'share_your_job_worth_report': 'あなたの仕事価値レポート',
'share_job_worth_report': '仕事価値レポート',
'share_custom_made': '「この仕事、割に合ってる?ブラック度診断」による分析',
'share_generating': '生成中...',
'share_download_report': 'レポートをダウンロード',
'share_basic_info': '基本情報',
'share_work_city': '勤務地',
'share_is_hometown': '地元',
'share_yes': 'はい',
'share_no': 'いいえ',
'share_daily_salary': '日給',
'share_day': '日',
'share_days': '日',
'share_work_hours_title': '勤務時間',
'share_hours': '時間',
'share_daily_work_hours': '1日の勤務時間',
'share_daily_commute_hours': '1日の通勤時間',
'share_rest_time': '休憩時間',
'share_weekly_work_days': '週の勤務日数',
'share_remote_work': 'リモートワーク',
'share_days_per_week': '日/週',
'share_shuttle_service': '送迎バス',
'share_annual_leave': '年次有給休暇',
'share_paid_sick_leave': '有給病気休暇',
'share_days_per_year': '日/年',
'share_work_environment_title': '職場環境',
'share_office_environment': 'オフィス環境',
'share_leadership_relation': '上司との関係',
'share_colleague_relationship': '同僚との関係',
'share_canteen_quality': '社員食堂',
'share_education_and_experience': '学歴と経験',
'share_highest_degree': '最終学歴',
'share_school_type_label': '大学タイプ',
'share_work_years_label': '勤務年数',
'share_contract_type_label': '雇用形態',
'share_final_assessment': '総合評価',
'share_low_value_assessment_1': 'この仕事はあなたにとって日々が苦痛で、まさにブラック企業の特徴を持っています。',
'share_low_value_assessment_2': 'この仕事は非常に厳しいですが、より良い将来へのステップになるかもしれません。',
'share_medium_value_assessment_1': 'この仕事は普通で、特に素晴らしいわけでも悪いわけでもありません。',
'share_medium_value_assessment_2': 'この仕事はそこそこ満足感を与えてくれる、悪くない選択です。',
'share_high_value_assessment_1': 'この仕事はあなたの期待のほとんどを満たし、やりがいを感じられます。',
'share_high_value_assessment_2': 'この仕事はあなたにぴったりで、チャレンジと報酬のバランスが取れています。',
'share_high_value_assessment_3': '理想的な仕事を見つけましたね!このような機会は滅多にありません!',
'share_working_days_per_year': '年間勤務日数',
'share_hometown_comment': '地元で働くことで、キャリアを追求しながら家族との繋がりも維持できるバランスの取れた生活を送れています。',
'share_tier1_city_comment': '生活費は高いですが、東京などの大都市では豊富な機会とネットワークがあなたのキャリア成長を加速させるでしょう。',
'share_tier2_city_comment': '県庁所在地クラスの都市では、適度な機会と比較的落ち着いた生活環境のバランスが取れています。',
'share_tier3_city_comment': '地方都市では生活コストが低く、ゆとりある生活が送れます。キャリア機会は限られるかもしれませんが、ストレスの少ない環境は大きな魅力です。',
'share_commute_short': '通勤時間が短いため、自己啓発やリラックスのための貴重な時間が確保できています。',
'share_commute_medium': '適度な通勤時間は負担にならず、オーディオブックや音楽を楽しむ時間として活用できます。',
'share_commute_long': '長時間の通勤は貴重な時間を消費し、心身の健康に影響を与える可能性があります。可能であれば引越しや在宅勤務の検討をおすすめします。',
'share_wfh_high': 'リモートワークの機会が多いため、通勤の負担が大幅に軽減され、生活の質が向上しています。',
'share_wfh_medium': '部分的なリモートワークにより、通勤時間を節約できています。',
'share_shuttle_service_good': '会社の送迎バスは価値ある福利厚生で、通勤をより快適にしています。',
'share_cbd_environment': '都心のオフィス環境は専門的かつ近代的で、ビジネスサービスやネットワーキングの機会へのアクセスが容易です。',
'share_factory_environment': '工場や屋外での勤務は独自の課題がありますが、耐久力と実践的な問題解決能力も育てています。',
'share_normal_environment': '職場環境は基本的な設備が整っており、生産的に仕事ができる条件が揃っています。',
'share_leadership_excellent': '上司に気に入られていることで優遇されていますが、それに伴う期待も高くなっています。',
'share_leadership_good': '理解のある上司はあなたの貢献を認め、必要なサポートを提供してくれます - これは職場での貴重な資産です。',
'share_leadership_normal': '上司との関係は事務的かつ機能的で、明確な期待と不必要な複雑さがない状態です。',
'share_leadership_strict': '厳しい管理の下で働くことは挑戦的ですが、専門性と細部への注意力を養うことができます。',
'share_leadership_bad': '上司との緊張関係は課題を生み出しますが、慎重なコミュニケーションと成果に焦点を当てることが重要です。',
'share_teamwork_excellent': '同僚との強い個人的なつながりは、仕事の満足度と効果を高めるサポートネットワークを作り出しています。',
'share_teamwork_good': '協力的なチーム環境では、相互サポートと効果的なコミュニケーションが促進され、日々の仕事がより快適で生産的になります。',
'share_teamwork_normal': '同僚との適度に専門的な関係を維持することで、仕事に集中しながらも必要なサポートを得ることができます。',
'share_teamwork_bad': '厳しいチームダイナミクスは適応性と自立性を必要とし、これが価値ある独立性と回復力を育てています。',
'share_workhours_balanced': 'バランスの取れた勤務スケジュールは、プライベートの時間を十分に確保でき、長期的なパフォーマンスの持続に貢献しています。',
'share_workhours_long': '長めの勤務時間は管理可能ですが、エネルギーを維持し燃え尽き症候群を防ぐための注意が必要です。',
'share_workhours_excessive': '勤務時間が非常に長く、持続不可能なレベルです。時間の経過とともに健康とパフォーマンスに影響を与える可能性があります。ワークロードの調整を検討してください。',
'share_rest_adequate': '十分な休憩時間は一日を通してエネルギーレベルと生産性を維持するのに役立っています。',
'share_rest_insufficient': '限られた休憩時間は、健康と集中力を維持するための短い動きの休憩を取り入れる必要性を示唆しています。',
'share_leave_abundant': '充実した休暇制度は、活力回復と個人的な活動のための十分な時間を提供し、持続的なモチベーションに不可欠です。',
'share_leave_limited': '休暇時間が限られている場合、その回復効果を最大化するための戦略的な計画が重要です。',
'share_phd_comment': '博士号は専門的なポジションへの扉を開き、高度な研究と分析能力を示しています。',
'share_masters_comment': '修士号は高度な知識と専門性を示し、今日の競争的な雇用市場で依然として価値があります。',
'share_bachelor_comment': '学士号は堅実な基盤を提供し、実践的な経験と組み合わせることで多様なキャリア機会を得られます。',
'share_below_bachelor_comment': '学士未満の学歴は一部の分野で課題となる場合がありますが、実践的なスキルと経験も同様に貴重な資産となります。',
'share_fresh_graduate_comment': '新卒として、あなたの新鮮な視点と最新の知識は資産であり、成長と学習の無限の可能性があります。',
'share_experienced_comment': '豊富な職務経験は、効果性と自信を高める貴重なコンテキストと判断力を提供します。',
'share_mid_career_comment': '数年の経験により、業界と個人の強みの両方を理解し、戦略的なキャリア開発の準備が整っています。',
'share_government_job_comment': '公的部門での雇用の安定性はキャリアの不確実性を軽減し、より自信を持った長期計画を可能にします。',
'share_private_job_comment': '民間部門での雇用にはある程度の不確実性がありますが、迅速な成長と報酬の機会を提供することが多いです。',
'share_salary_high_cny': '日給は競争力があり、基本的な必需品と裁量支出の両方に対する財政的安全性と柔軟性を提供しています。',
'share_salary_medium_cny': '給与は基本的なニーズを快適に満たしていますが、最適な財政状態のためには計画的な予算管理が必要です。',
'share_salary_low_cny': '現在の給与水準は慎重な財務管理を必要とし、収入増加の機会を模索すべきでしょう。',
'share_salary_high_foreign': '日給は競争力があり、基本的な必需品と裁量支出の両方に対する財政的安全性と柔軟性を提供しています。',
'share_salary_medium_foreign': '給与は基本的なニーズを快適に満たしていますが、最適な財政状態のためには計画的な予算管理が必要です。',
'share_salary_low_foreign': '現在の給与水準は慎重な財務管理を必要とし、収入増加の機会を模索すべきでしょう。',
'share_high_cost_city': '生活費の高い地域では、給与の価値を最大化するための慎重な財務計画が役立ちます。',
'share_low_cost_city': '生活費の低い地域では、給与のより大きな購買力と貯蓄の可能性が得られます。',
'share_value_low': '現在のポジションは限られた価値を示していますが、将来の成長に必要な経験を提供するかもしれません。次のキャリア展開を準備しながら、あらゆる側面から学びを得てください。',
'share_value_medium': 'あなたの仕事は、強みと改善の余地の両方を持つバランスの取れた価値を提供しています。ポジティブな側面を活かしながら、課題に対処する戦略を開発してください。',
'share_value_high': '高価値のポジションを見つけました。維持し発展させる価値があります。あなたの強みを継続的に伸ばし、この役割がもたらす満足感を大切にしてください。',
'share_summary_advice': '全体的な推奨事項',
// 評価
'rating_enter_salary': '給与を入力してください',
'rating_terrible': '最悪',
'rating_poor': '悪い',
'rating_average': '普通',
'rating_good': '良い',
'rating_great': '素晴らしい',
'rating_excellent': '非常に優れている',
'rating_perfect': '理想的',
'share_country': '勤務国・地域',
}
};
@@ -486,7 +709,7 @@ export const LanguageProvider: React.FC<{children: ReactNode}> = ({ children })
// 首次渲染时检查本地存储的语言设置
useEffect(() => {
const savedLanguage = localStorage.getItem('language') as Language;
if (savedLanguage && (savedLanguage === 'zh' || savedLanguage === 'en')) {
if (savedLanguage && (savedLanguage === 'zh' || savedLanguage === 'en' || savedLanguage === 'ja')) {
setLanguageState(savedLanguage);
}
}, []);

View File

@@ -26,7 +26,17 @@ export const LanguageSwitcher: React.FC = () => {
: 'bg-gray-100 text-gray-800 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600'
}`}
>
En
English
</button>
<button
onClick={() => setLanguage('ja')}
className={`px-1.5 py-0.5 text-xs font-medium rounded-md transition-colors ${
language === 'ja'
? 'bg-blue-500 text-white dark:bg-blue-600'
: 'bg-gray-100 text-gray-800 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600'
}`}
>
</button>
</div>
);

View File

@@ -21,6 +21,7 @@ interface ShareCardProps {
workDaysPerYear: string;
countryCode: string;
countryName: string;
currencySymbol: string;
// 详细工作信息
workDaysPerWeek: string;
@@ -48,6 +49,12 @@ interface ShareCardProps {
// 将中文评级转换为翻译键
const getAssessmentKey = (assessment: string): string => {
// 如果已经是翻译键,直接返回
if (assessment.startsWith('rating_')) {
return assessment;
}
// 否则,将中文评级转换为翻译键
switch (assessment) {
case '惨绝人寰': return 'rating_terrible';
case '略惨': return 'rating_poor';
@@ -466,7 +473,7 @@ const ShareCard: React.FC<ShareCardProps> = (props) => {
content: salaryComment,
emoji: "💰",
details: [
{ label: t('share_daily_salary'), value: `${isYuan ? '¥' : '$'}${dailySalary}/${t('share_day')}` },
{ label: t('share_daily_salary'), value: `${props.currencySymbol}${dailySalary}/${t('share_day')}` },
{ label: t('share_working_days_per_year'), value: `${props.workDaysPerYear} ${t('share_days')}` }
]
});
@@ -651,7 +658,7 @@ const ShareCard: React.FC<ShareCardProps> = (props) => {
</div>
<div className="bg-gray-50 p-3 rounded-lg">
<div className="text-sm text-gray-500">{t('share_daily_salary')}</div>
<div className="font-medium text-gray-800 mt-1">{props.isYuan === 'true' ? '¥' : '$'}{props.dailySalary}/{t('share_day')}</div>
<div className="font-medium text-gray-800 mt-1">{props.currencySymbol}{props.dailySalary}/{t('share_day')}</div>
</div>
<div className="bg-gray-50 p-3 rounded-lg">
<div className="text-sm text-gray-500">{t('share_working_days_per_year')}</div>

View File

@@ -370,6 +370,299 @@ const countryNamesEn: Record<string, string> = {
'ZW': 'Zimbabwe'
};
// 为日语界面添加国家名称
const countryNamesJp: Record<string, string> = {
'AF': 'アフガニスタン',
'AL': 'アルバニア',
'DZ': 'アルジェリア',
'AO': 'アンゴラ',
'AR': 'アルゼンチン',
'AM': 'アルメニア',
'AU': 'オーストラリア',
'AT': 'オーストリア',
'AZ': 'アゼルバイジャン',
'BI': 'ブルンジ',
'BE': 'ベルギー',
'BJ': 'ベナン',
'BF': 'ブルキナファソ',
'BD': 'バングラデシュ',
'BG': 'ブルガリア',
'BH': 'バーレーン',
'BS': 'バハマ',
'BA': 'ボスニア・ヘルツェゴビナ',
'BY': 'ベラルーシ',
'BZ': 'ベリーズ',
'BO': 'ボリビア',
'BR': 'ブラジル',
'BB': 'バルバドス',
'BN': 'ブルネイ',
'BT': 'ブータン',
'BW': 'ボツワナ',
'CF': '中央アフリカ共和国',
'CA': 'カナダ',
'CH': 'スイス',
'CL': 'チリ',
'CN': '中国',
'CI': 'コートジボワール',
'CM': 'カメルーン',
'CD': 'コンゴ民主共和国',
'CG': 'コンゴ共和国',
'CO': 'コロンビア',
'KM': 'コモロ',
'CV': 'カーボベルデ',
'CR': 'コスタリカ',
'CY': 'キプロス',
'CZ': 'チェコ共和国',
'DE': 'ドイツ',
'DJ': 'ジブチ',
'DM': 'ドミニカ国',
'DK': 'デンマーク',
'DO': 'ドミニカ共和国',
'EC': 'エクアドル',
'EG': 'エジプト',
'ES': 'スペイン',
'EE': 'エストニア',
'ET': 'エチオピア',
'FI': 'フィンランド',
'FJ': 'フィジー',
'FR': 'フランス',
'GA': 'ガボン',
'GB': 'イギリス',
'GE': 'ジョージア',
'GH': 'ガーナ',
'GN': 'ギニア',
'GM': 'ガンビア',
'GW': 'ギニアビサウ',
'GQ': '赤道ギニア',
'GR': 'ギリシャ',
'GD': 'グレナダ',
'GT': 'グアテマラ',
'GY': 'ガイアナ',
'HK': '香港特別行政区',
'HN': 'ホンジュラス',
'HR': 'クロアチア',
'HT': 'ハイチ',
'HU': 'ハンガリー',
'ID': 'インドネシア',
'IN': 'インド',
'IE': 'アイルランド',
'IR': 'イラン',
'IQ': 'イラク',
'IS': 'アイスランド',
'IL': 'イスラエル',
'IT': 'イタリア',
'JM': 'ジャマイカ',
'JO': 'ヨルダン',
'JP': '日本',
'KZ': 'カザフスタン',
'KE': 'ケニア',
'KG': 'キルギス',
'KH': 'カンボジア',
'KI': 'キリバス',
'KN': 'セントクリストファー・ネイビス',
'KR': '韓国',
'LA': 'ラオス',
'LB': 'レバノン',
'LR': 'リベリア',
'LY': 'リビア',
'LC': 'セントルシア',
'LK': 'スリランカ',
'LS': 'レソト',
'LT': 'リトアニア',
'LU': 'ルクセンブルク',
'LV': 'ラトビア',
'MO': 'マカオ特別行政区',
'MA': 'モロッコ',
'MD': 'モルドバ',
'MG': 'マダガスカル',
'MV': 'モルディブ',
'MX': 'メキシコ',
'MK': '北マケドニア',
'ML': 'マリ',
'MT': 'マルタ',
'MM': 'ミャンマー',
'ME': 'モンテネグロ',
'MN': 'モンゴル',
'MZ': 'モザンビーク',
'MR': 'モーリタニア',
'MU': 'モーリシャス',
'MW': 'マラウイ',
'MY': 'マレーシア',
'NA': 'ナミビア',
'NE': 'ニジェール',
'NG': 'ナイジェリア',
'NI': 'ニカラグア',
'NL': 'オランダ',
'NO': 'ノルウェー',
'NP': 'ネパール',
'NZ': 'ニュージーランド',
'PK': 'パキスタン',
'PA': 'パナマ',
'PE': 'ペルー',
'PH': 'フィリピン',
'PG': 'パプアニューギニア',
'PL': 'ポーランド',
'PR': 'プエルトリコ',
'PT': 'ポルトガル',
'PY': 'パラグアイ',
'PS': 'パレスチナ',
'QA': 'カタール',
'RO': 'ルーマニア',
'RU': 'ロシア',
'RW': 'ルワンダ',
'SA': 'サウジアラビア',
'SD': 'スーダン',
'SN': 'セネガル',
'SG': 'シンガポール',
'SB': 'ソロモン諸島',
'SL': 'シエラレオネ',
'SV': 'エルサルバドル',
'SO': 'ソマリア',
'RS': 'セルビア',
'ST': 'サントメ・プリンシペ',
'SR': 'スリナム',
'SK': 'スロバキア',
'SI': 'スロベニア',
'SE': 'スウェーデン',
'SZ': 'エスワティニ',
'SC': 'セーシェル',
'TC': 'タークス・カイコス諸島',
'TD': 'チャド',
'TG': 'トーゴ',
'TH': 'タイ',
'TJ': 'タジキスタン',
'TL': '東ティモール',
'TT': 'トリニダード・トバゴ',
'TN': 'チュニジア',
'TR': 'トルコ',
'TV': 'ツバル',
'TZ': 'タンザニア',
'UG': 'ウガンダ',
'UA': 'ウクライナ',
'UY': 'ウルグアイ',
'US': 'アメリカ合衆国',
'UZ': 'ウズベキスタン',
'VC': 'セントビンセント・グレナディーン',
'VN': 'ベトナム',
'VU': 'バヌアツ',
'XK': 'コソボ',
'ZA': '南アフリカ',
'ZM': 'ザンビア',
'ZW': 'ジンバブエ'
};
// 添加各国货币符号映射
const currencySymbols: Record<string, string> = {
'AF': '؋', // 阿富汗尼
'AL': 'L', // 阿尔巴尼亚列克
'DZ': 'د.ج', // 阿尔及利亚第纳尔
'AO': 'Kz', // 安哥拉宽扎
'AR': '$', // 阿根廷比索
'AM': '֏', // 亚美尼亚德拉姆
'AU': 'A$', // 澳大利亚元
'AT': '€', // 欧元
'AZ': '₼', // 阿塞拜疆马纳特
'BI': 'FBu', // 布隆迪法郎
'BE': '€', // 欧元
'BJ': 'CFA', // 西非法郎
'BF': 'CFA', // 西非法郎
'BD': '৳', // 孟加拉塔卡
'BG': 'лв', // 保加利亚列弗
'BH': '.د.ب', // 巴林第纳尔
'BS': 'B$', // 巴哈马元
'BA': 'KM', // 波黑可兑换马克
'BY': 'Br', // 白俄罗斯卢布
'BZ': 'BZ$', // 伯利兹元
'BO': 'Bs', // 玻利维亚诺
'BR': 'R$', // 巴西雷亚尔
'BB': 'Bds$', // 巴巴多斯元
'BN': 'B$', // 文莱元
'BT': 'Nu.', // 不丹努扎姆
'BW': 'P', // 博茨瓦纳普拉
'CA': 'C$', // 加拿大元
'CH': 'CHF', // 瑞士法郎
'CL': 'CLP$', // 智利比索
'CN': '¥', // 人民币
'CI': 'CFA', // 西非法郎
'CM': 'FCFA', // 中非法郎
'CD': 'FC', // 刚果法郎
'CG': 'FCFA', // 中非法郎
'CO': 'Col$', // 哥伦比亚比索
'CR': '₡', // 哥斯达黎加科朗
'CY': '€', // 欧元
'CZ': 'Kč', // 捷克克朗
'DE': '€', // 欧元
'DK': 'kr', // 丹麦克朗
'DO': 'RD$', // 多米尼加比索
'EC': '$', // 美元(厄瓜多尔使用美元)
'EG': 'E£', // 埃及镑
'ES': '€', // 欧元
'EE': '€', // 欧元
'ET': 'Br', // 埃塞俄比亚比尔
'FI': '€', // 欧元
'FJ': 'FJ$', // 斐济元
'FR': '€', // 欧元
'GB': '£', // 英镑
'GE': '₾', // 格鲁吉亚拉里
'GH': '₵', // 加纳塞地
'GR': '€', // 欧元
'GT': 'Q', // 危地马拉格查尔
'HK': 'HK$', // 港元
'HN': 'L', // 洪都拉斯伦皮拉
'HR': '€', // 欧元克罗地亚自2023年加入欧元区
'HU': 'Ft', // 匈牙利福林
'ID': 'Rp', // 印尼盾
'IN': '₹', // 印度卢比
'IE': '€', // 欧元
'IR': '﷼', // 伊朗里亚尔
'IQ': 'ع.د', // 伊拉克第纳尔
'IS': 'kr', // 冰岛克朗
'IL': '₪', // 以色列新谢克尔
'IT': '€', // 欧元
'JM': 'J$', // 牙买加元
'JO': 'JD', // 约旦第纳尔
'JP': '¥', // 日元
'KE': 'KSh', // 肯尼亚先令
'KR': '₩', // 韩元
'KW': 'د.ك', // 科威特第纳尔
'LB': 'L£', // 黎巴嫩镑
'LK': 'Rs', // 斯里兰卡卢比
'LT': '€', // 欧元
'LU': '€', // 欧元
'LV': '€', // 欧元
'MA': 'د.م.', // 摩洛哥迪拉姆
'MX': 'Mex$', // 墨西哥比索
'MY': 'RM', // 马来西亚林吉特
'NG': '₦', // 尼日利亚奈拉
'NL': '€', // 欧元
'NO': 'kr', // 挪威克朗
'NP': 'रू', // 尼泊尔卢比
'NZ': 'NZ$', // 新西兰元
'PK': '₨', // 巴基斯坦卢比
'PA': 'B/.', // 巴拿马巴波亚
'PE': 'S/.', // 秘鲁索尔
'PH': '₱', // 菲律宾比索
'PL': 'zł', // 波兰兹罗提
'PT': '€', // 欧元
'QA': 'ر.ق', // 卡塔尔里亚尔
'RO': 'lei', // 罗马尼亚列伊
'RU': '₽', // 俄罗斯卢布
'SA': 'ر.س', // 沙特里亚尔
'SG': 'S$', // 新加坡元
'SK': '€', // 欧元
'SI': '€', // 欧元
'SE': 'kr', // 瑞典克朗
'TH': '฿', // 泰铢
'TR': '₺', // 土耳其里拉
'TW': 'NT$', // 新台币
'UA': '₴', // 乌克兰格里夫纳
'US': '$', // 美元
'UY': '$U', // 乌拉圭比索
'VN': '₫', // 越南盾
'ZA': 'R', // 南非兰特
// 默认其他国家使用美元符号
};
// 定义表单数据接口
interface FormData {
salary: string;
@@ -677,6 +970,18 @@ const SalaryCalculator = () => {
if (value <= 4.0) return { text: t('rating_excellent'), color: "text-purple-500" };
return { text: t('rating_perfect'), color: "text-yellow-400" };
};
// 获取评级的翻译键,用于分享链接
const getValueAssessmentKey = () => {
if (!formData.salary) return 'rating_enter_salary';
if (value < 0.6) return 'rating_terrible';
if (value < 1.0) return 'rating_poor';
if (value <= 1.8) return 'rating_average';
if (value <= 2.5) return 'rating_good';
if (value <= 3.2) return 'rating_great';
if (value <= 4.0) return 'rating_excellent';
return 'rating_perfect';
};
const RadioGroup = ({ label, name, value, onChange, options }: {
label: string;
@@ -774,9 +1079,17 @@ const SalaryCalculator = () => {
if (language === 'en') {
return countryNamesEn[countryCode] || pppFactors[countryCode]?.name || 'Unknown';
}
if (language === 'ja') {
return countryNamesJp[countryCode] || pppFactors[countryCode]?.name || '不明';
}
return pppFactors[countryCode]?.name || 'Unknown';
}, [language]);
// 获取当前选择国家的货币符号
const getCurrencySymbol = useCallback((countryCode: string) => {
return currencySymbols[countryCode] || '$'; // 如果没有找到对应货币符号,默认使用美元符号
}, []);
return (
<div className="max-w-2xl mx-auto p-4 sm:p-6">
<div className="mb-4 text-center">
@@ -795,7 +1108,7 @@ const SalaryCalculator = () => {
</div>
<div className="flex items-center justify-center gap-3 mb-2">
<p className="text-sm text-gray-500 dark:text-gray-400">{t('version')}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">v5.1.0</p>
<a
href="https://github.com/zippland/worth-calculator"
target="_blank"
@@ -1192,7 +1505,7 @@ const SalaryCalculator = () => {
<div>
<div className="text-sm font-medium text-gray-500 dark:text-gray-400">{t('average_daily_salary')}</div>
<div className="text-2xl font-semibold mt-1 text-gray-900 dark:text-white">
{selectedCountry !== 'CN' ? '$' : '¥'}{getDisplaySalary()}
{getCurrencySymbol(selectedCountry)}{getDisplaySalary()}
</div>
</div>
<div>
@@ -1211,7 +1524,7 @@ const SalaryCalculator = () => {
pathname: '/share',
query: {
value: value.toFixed(2),
assessment: getValueAssessment().text,
assessment: getValueAssessmentKey(),
assessmentColor: getValueAssessment().color,
cityFactor: formData.cityFactor,
workHours: formData.workHours,
@@ -1238,7 +1551,8 @@ const SalaryCalculator = () => {
jobStability: formData.jobStability,
bachelorType: formData.bachelorType,
countryCode: selectedCountry,
countryName: getCountryName(selectedCountry)
countryName: getCountryName(selectedCountry),
currencySymbol: getCurrencySymbol(selectedCountry)
}
}}
className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors