From 78b6eab62bf561851f00b1e0f68ec57e0b9359e7 Mon Sep 17 00:00:00 2001 From: SevicheCC <91365763+Sevichecc@users.noreply.github.com> Date: Fri, 29 Dec 2023 02:15:41 +0800 Subject: [PATCH] Add i18n support and translation for zh-CN (#394) Relative issue: #223 --------- Co-authored-by: Erik Vroon Co-authored-by: Erik Vroon --- frontend/next-i18next.config.js | 7 + frontend/next.config.js | 2 + frontend/package.json | 3 + frontend/public/locales/en/common.json | 218 +++++++ frontend/public/locales/zh-CN/common.json | 218 +++++++ frontend/src/components/brackets/brackets.tsx | 38 +- frontend/src/components/builder/builder.tsx | 13 +- .../buttons/create_matches_auto.tsx | 4 +- .../src/components/buttons/create_stage.tsx | 5 +- .../components/buttons/next_stage_button.tsx | 8 +- .../forms/player_create_csv_input.tsx | 11 +- frontend/src/components/forms/user.tsx | 20 +- frontend/src/components/info/player_list.tsx | 5 +- .../modals/activate_next_round_modal.tsx | 24 +- frontend/src/components/modals/club_modal.tsx | 12 +- .../components/modals/create_stage_item.tsx | 35 +- .../src/components/modals/match_modal.tsx | 37 +- .../components/modals/player_create_modal.tsx | 32 +- .../components/modals/player_update_modal.tsx | 16 +- .../src/components/modals/round_modal.tsx | 19 +- frontend/src/components/modals/spotlight.tsx | 42 +- .../components/modals/team_create_modal.tsx | 30 +- .../components/modals/team_update_modal.tsx | 20 +- .../components/modals/tournament_modal.tsx | 42 +- .../src/components/modals/update_stage.tsx | 6 +- .../components/modals/update_stage_item.tsx | 6 +- .../src/components/navbar/_main_links.tsx | 40 +- .../scheduling/settings/ladder_fixed.tsx | 15 +- frontend/src/components/tables/clubs.tsx | 8 +- frontend/src/components/tables/courts.tsx | 8 +- frontend/src/components/tables/players.tsx | 23 +- frontend/src/components/tables/standings.tsx | 10 +- frontend/src/components/tables/teams.tsx | 25 +- .../src/components/tables/tournaments.tsx | 12 +- .../components/tables/upcoming_matches.tsx | 20 +- .../src/components/utils/empty_table_info.tsx | 4 +- frontend/src/components/utils/file_upload.tsx | 10 +- frontend/src/components/utils/password.tsx | 41 +- frontend/src/pages/404.tsx | 30 +- frontend/src/pages/_app.tsx | 41 +- frontend/src/pages/clubs.tsx | 12 +- frontend/src/pages/create_account.tsx | 33 +- frontend/src/pages/index.tsx | 13 +- frontend/src/pages/login.tsx | 33 +- frontend/src/pages/tournaments/[id].tsx | 21 +- .../src/pages/tournaments/[id]/courts.tsx | 19 +- .../tournaments/[id]/dashboard/courts.tsx | 13 +- .../tournaments/[id]/dashboard/index.tsx | 7 + .../src/pages/tournaments/[id]/players.tsx | 11 +- .../src/pages/tournaments/[id]/schedule.tsx | 23 +- .../src/pages/tournaments/[id]/settings.tsx | 58 +- .../src/pages/tournaments/[id]/stages.tsx | 7 + frontend/src/pages/tournaments/[id]/teams.tsx | 16 +- frontend/src/pages/user.tsx | 11 +- frontend/yarn.lock | 547 ++++++++++-------- 55 files changed, 1374 insertions(+), 610 deletions(-) create mode 100644 frontend/next-i18next.config.js create mode 100644 frontend/public/locales/en/common.json create mode 100644 frontend/public/locales/zh-CN/common.json diff --git a/frontend/next-i18next.config.js b/frontend/next-i18next.config.js new file mode 100644 index 000000000..bc66d834a --- /dev/null +++ b/frontend/next-i18next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next-i18next').UserConfig} */ +module.exports = { + i18n: { + locales: ['en', 'zh-CN'], + defaultLocale: 'en', + }, +}; diff --git a/frontend/next.config.js b/frontend/next.config.js index 7ce9ff4b8..7c8e2314b 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,3 +1,4 @@ +const { i18n } = require('./next-i18next.config.js'); const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); @@ -7,4 +8,5 @@ module.exports = withBundleAnalyzer({ eslint: { ignoreDuringBuilds: true, }, + i18n, }); diff --git a/frontend/package.json b/frontend/package.json index 14d00f7f2..59ec0d5f8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,10 +37,13 @@ "cookies-next": "^4.1.0", "date-fns": "^3.0.1", "dayjs": "^1.11.10", + "i18next": "^23.7.11", "next": "^14.0.3", + "next-i18next": "^15.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-ellipsis-text": "^1.2.1", + "react-i18next": "^13.5.0", "react-icons": "^4.12.0", "react-qr-code": "^2.0.12", "react-redux": "^9.0.2", diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json new file mode 100644 index 000000000..9925e481e --- /dev/null +++ b/frontend/public/locales/en/common.json @@ -0,0 +1,218 @@ +{ + "not_found_title": "You have found a secret place.", + "not_found_description": "Unfortunately, this is only a 404 page. You may have mistyped the address, or the page has been moved to another URL.", + "back_home_nav": "Take me back to home page", + "clubs_title": "Clubs", + "empty_name_validation": "Name cannot be empty", + "empty_email_validation": "Invalid email", + "empty_password_validation": "Password cannot be empty", + "too_short_name_validation": "Name is too short", + "too_short_dashboard_link_validation": "Dashboard link is short", + "too_short_password_validation": "Password is too short", + "invalid_email_validation": "Invalid email", + "invalid_password_validation": "Invalid password", + "create_account_title": "Create a new account", + "create_account_alert_title": "Unavailable", + "create_account_alert_description": "Account creation is disabled on this domain for now since bracket is still in beta phase", + "create_account_button": "Create Account", + "email_input_label": "Email Address", + "email_input_placeholder": "Your email", + "name_input_label": "Name", + "name_input_placeholder": "Your name", + "password_input_label": "Password", + "password_input_placeholder": "Your password", + "back_to_login_nav": "Back to login page", + "tournaments_title": "Tournaments", + "tournament_title": "Tournament", + "login_success_title": "Login successful", + "welcome_title": "Welcome to", + "sign_in_title": "Sign in", + "forgot_password_button": "Forgot password?", + "edit_profile_title": "Edit Profile", + "match_filter_option_all": "All matches", + "match_filter_option_past": "Hide past matches", + "match_filter_option_current": "Current matches", + "name_filter_options_team": "Team names", + "name_filter_options_player": "Player names", + "view_dashboard_button": "View Dashboard", + "add_court_title": "Add Court", + "create_court_button": "Create Court", + "court_name_input_placeholder": "Best Court Ever", + "player_title": "Players", + "no_matches_description": "No matches yet", + "drop_match_alert_title": "Drop a match here", + "schedule_title": "Schedule", + "schedule_description": "Schedule all unscheduled matches", + "club_choose_title": "Please choose a club", + "start_time_choose_title": "Please choose a start time", + "duration_minutes_choose_title": "Please choose a duration of the matches", + "margin_minutes_choose_title": "Please choose a margin between matches", + "tournament_name_input_placeholder": "Best Tournament Ever", + "club_select_placeholder": "Pick a club for this tournament", + "planning_of_matches_legend": "Planning of matches", + "planning_of_matches_description": "Start of the tournament", + "match_duration_label": "Match duration (minutes)", + "time_between_matches_label": "Time between matches (minutes)", + "dashboard_settings_title": "Dashboard Settings", + "dashboard_link_label": "Dashboard link", + "dashboard_link_placeholder": "best_tournament", + "dashboard_public_description": "Allow anyone to see the dashboard of rounds and matches", + "miscellaneous_title": "Miscellaneous", + "miscellaneous_label": "Allow players to be in multiple teams", + "auto_assign_courts_label": "Automatically assign courts to matches", + "save_button": "Save", + "copy_dashboard_url_button": "Copy dashboard URL", + "copied_dashboard_url_button": "Copied dashboard URL", + "filter_stage_item_label": "Filter on stage item", + "filter_stage_item_placeholder": "No filter", + "team_title": "Team", + "current_matches_badge": "Current matches", + "next_matches_badge": "Next matches", + "no_round_title": "No round", + "no_round_description": "There are no rounds in this stage item yet", + "add_round_button": "Add Round", + "no_round_found_title": "No rounds found", + "no_round_found_description": "Please wait for the organiser to add them.", + "no_round_found_in_stage_description": "There are no rounds in this stage yet", + "tournament_not_started_title": "Tournament has not started yet", + "tournament_not_started_description": "Please wait for the tournament to start.", + "edit_name_button": "Edit name", + "delete_button": "Delete", + "active_badge_label": "Active", + "auto_create_matches_button": "Add new matches automatically", + "add_stage_button": "Add stage", + "next_stage_button": "Go to next stage", + "previous_stage_button": "Go to previous stage", + "multiple_teams_input_label": "Add multiple teams. Put every team on a separate line", + "multiple_teams_input_placeholder": "Team 1", + "multiple_players_input_label": "Add multiple players. Put every player on a separate line", + "multiple_players_input_placeholder": "Player 1", + "edit_details_tab_title": "Edit details", + "edit_password_tab_title": "Edit password", + "no_team_members_description": "No members", + "active_next_round_modal_title": "Assign times and courts to matches of next round", + "active_next_round_modal_description": "This will assign times and courts to matches of next round, which is the round after the current activated (green) round.", + "active_next_round_modal_choose_description": "You can choose to either (check the checkbox or not):", + "checkbox_status_checked": "Checked", + "checkbox_status_unchecked": "Unchecked", + "active_next_round_modal_choose_option_unchecked": "Use default timing (the next matches will be planned tightly after the matches of the active round end, taking margin into account)", + "active_next_round_modal_choose_option_checked": "Adjust the start times of the next matches to start immediately(now). This will be done by modifying the margin times of the matches in the previous round.", + "adjust_start_times_checkbox_label": "Adjust start time of matches in this round to the current time", + "plan_next_round_button": "Plan next round", + "edit_club_button": "Edit club", + "create_club_button": "Create club", + "club_name_input_placeholder": "Best Club Ever", + "team_count_select_elimination_label": "Number of teams advancing from the previous stage", + "team_count_select_elimination_placeholder": "2, 4, 8 etc.", + "team_count_input_round_robin_label": "Number of teams advancing from the previous stage", + "none": "None", + "at_least_two_team_validation": "Need at least two teams", + "stage_type_select_label": "Stage type", + "round_robin_label": "Round Robin", + "single_elimination_label": "Single Elimination", + "swiss_label": "Swiss", + "create_stage_item_button": "Create Stage Item", + "add_stage_item_modal_title": "Add stage item", + "remove_match_button": "Remove Match", + "negative_score_validation": "Score cannot be negative", + "negative_match_duration_validation": "Match duration cannot be negative", + "negative_match_margin_validation": "Match margin cannot be negative", + "edit_match_modal_title": "Edit Match", + "score_of_label": "Score of", + "custom_match_duration_label": "Custom match duration", + "customize_checkbox_label": "Customize", + "custom_match_margin_label": "Custom match margin", + "minutes": "minutes", + "at_least_one_player_validation": "Enter at least one player", + "active_players_checkbox_label": "These players are active", + "active_player_checkbox_label": "This player is active", + "player_name_input_placeholder": "Best Player Ever", + "save_players_button": "Save players", + "single_player_title": "Single Player", + "multiple_players_title": "Multiple Players", + "create_player_modal_title": "Create Player", + "add_player_button": "Add Player", + "edit_player": "Edit Player", + "delete_round_button": "Delete Round", + "edit_round": "Edit Round", + "round_name_input_placeholder": "Best Round Ever", + "active_round_checkbox_label": "This round is active", + "draft_round_checkbox_label": "This round is a draft round", + "home_title": "Home", + "home_spotlight_description": "Get to home page", + "clubs_spotlight_description": "View, add or delete clubs", + "user_settings_title": "User Settings", + "user_settings_spotlight_description": "Change name, email, password etc.", + "planning_title": "planning", + "planning_spotlight_description": "Change planning of matches", + "teams_title": "Teams", + "teams_spotlight_description": "View, add or delete teams", + "players_title": "Players", + "players_spotlight_description": "View, add or delete players", + "stage_title": "Stage", + "stage_spotlight_description": "Change the layout of the tournament", + "court_title": "Courts", + "court_spotlight_description": "View, add or delete courts", + "tournament_setting_title": "Tournament Settings", + "tournament_setting_spotlight_description": "Change the settings of the tournament", + "nothing_found_placeholder": "Nothing found ...", + "search_placeholder": "Search ...", + "at_least_one_team_validation": "Enter at least one team", + "active_teams_checkbox_label": "These teams are active", + "team_name_input_placeholder": "Best Team Ever", + "active_team_checkbox_label": "This team is active", + "team_member_select_placeholder": "Pick all that you like", + "team_member_select_title": "Team members", + "single_team": "Single Team", + "multiple_teams": "Multiple Teams", + "add_team_button": "Add Team", + "edit_team_title": "Edit Team", + "club_select_label": "Club", + "now_button": "NOW", + "logout_title": "Logout", + "user_title": "User", + "more_title": "More", + "website_title": "Website", + "github_title": "Github", + "api_docs_title": "API docs", + "elo_input_label": "Max ELO difference", + "only_recommended_input_group_label": "Only show teams that played less matches", + "only_recommended_radio_label": "Only recommended", + "all_matches_radio_label": "All matches", + "max_results_input_label": "Max results", + "iterations_input_label": "Iterations", + "delete_club_button": "Delete Club", + "title": "Title", + "name_field_text": "name", + "delete_court_button": "Delete Court", + "win_distribution_text_win": "wins", + "win_distribution_text_draws": "draws", + "win_distribution_text_losses": "losses", + "delete_player_button": "Delete Player", + "status": "Status", + "created": "Created", + "elo_score": "ELO score", + "swiss_score": "Swiss score", + "name_table_header": "Name", + "member_table_header": "Members", + "active": "Active", + "inactive": "Inactive", + "delete_team_button": "Delete Team", + "delete_tournament_button": "Delete Tournament", + "edit_tournament_button": "Edit Tournament", + "recommended_badge_title": "Recommended", + "upcoming_matches_empty_table_info": "upcoming matches", + "elo_difference": "ELO Difference", + "swiss_difference": "Swiss Difference", + "could_not_find_any_alert": "Could not find any", + "dropzone_accept_text": "Drop files here", + "dropzone_reject_text": "Image must be less than 10MB", + "dropzone_idle_text": "Upload logo", + "upload_placeholder": "Drop a file here to upload as tournament logo.", + "number_required": "Includes number", + "lowercase_required": "Includes lowercase letter", + "uppercase_required": "Includes uppercase letter", + "special_character_required": "Includes special character", + "8_characters_required": "Has at least 8 characters", + "create_tournament_button": "Create Tournament" +} \ No newline at end of file diff --git a/frontend/public/locales/zh-CN/common.json b/frontend/public/locales/zh-CN/common.json new file mode 100644 index 000000000..2ed8202ef --- /dev/null +++ b/frontend/public/locales/zh-CN/common.json @@ -0,0 +1,218 @@ +{ + "not_found_title": "你找到了一个秘密地方。", + "not_found_description": "不幸的是,这只是一个404页面。你可能输入了错误的地址,或者页面已经被移动到另一个URL。", + "back_home_nav": "带我回到主页", + "clubs_title": "俱乐部", + "empty_name_validation": "名称不能为空", + "empty_email_validation": "无效的电子邮件", + "empty_password_validation": "密码不能为空", + "too_short_name_validation": "名称太短", + "too_short_dashboard_link_validation": "仪表板链接太短", + "too_short_password_validation": "密码太短", + "invalid_email_validation": "无效的电子邮件", + "invalid_password_validation": "无效的密码", + "create_account_title": "创建一个新的计数", + "create_account_alert_title": "不可用", + "create_account_alert_description": "由于括号仍处于测试阶段,因此暂时禁用了此域上的帐户创建", + "create_account_button": "创建帐户", + "email_input_label": "电子邮件地址", + "email_input_placeholder": "你的电子邮件", + "name_input_label": "名称", + "name_input_placeholder": "你的名称", + "password_input_label": "密码", + "password_input_placeholder": "你的密码", + "back_to_login_nav": "返回登录页面", + "tournaments_title": "锦标赛", + "tournament_title": "锦标赛", + "login_success_title": "登录成功", + "welcome_title": "欢迎来到", + "sign_in_title": "登录", + "forgot_password_button": "忘记密码?", + "edit_profile_title": "编辑个人资料", + "match_filter_option_all": "所有比赛", + "match_filter_option_past": "隐藏过去的比赛", + "match_filter_option_current": "当前的比赛", + "name_filter_options_team": "团队名称", + "name_filter_options_player": "玩家名称", + "view_dashboard_button": "查看仪表板", + "add_court_title": "添加法院", + "create_court_button": "创建法院", + "court_name_input_placeholder":"最好的法院", + "player_title": "玩家", + "no_matches_description": "还没有比赛", + "drop_match_alert_title": "在这里放下一场比赛", + "schedule_title": "时间表", + "schedule_description": "安排所有未安排的比赛", + "club_choose_title": "请选择一个俱乐部", + "start_time_choose_title": "请选择开始时间", + "duration_minutes_choose_title": "请选择比赛的持续时间", + "margin_minutes_choose_title": "请选择比赛之间的间隔", + "tournament_name_input_placeholder": "最佳锦标赛", + "club_select_placeholder": "为此锦标赛选择一个俱乐部", + "planning_of_matches_legend": "比赛规划", + "planning_of_matches_description": "锦标赛开始", + "match_duration_label": "比赛持续时间(分钟)", + "time_between_matches_label": "比赛间隔时间(分钟)", + "dashboard_settings_title": "仪表板设置", + "dashboard_link_label": "仪表板链接", + "dashboard_link_placeholder": "最佳锦标赛", + "dashboard_public_description": "允许任何人查看轮次和比赛的仪表板", + "miscellaneous_title": "杂项", + "miscellaneous_label": "允许玩家加入多个队伍", + "auto_assign_courts_label": "自动为比赛分配场地", + "save_button": "保存", + "copy_dashboard_url_button": "复制仪表板URL", + "copied_dashboard_url_button": "已复制仪表板URL", + "filter_stage_item_label": "筛选阶段项目", + "filter_stage_item_placeholder": "无筛选", + "team_title": "队伍", + "current_matches_badge": "当前比赛", + "next_matches_badge": "下一场比赛", + "no_round_title": "无轮次", + "no_round_description": "此阶段项目尚无轮次", + "add_round_button": "添加轮次", + "no_round_found_title": "未找到轮次", + "no_round_found_description": "请等待组织者添加。", + "no_round_found_in_stage_description": "此阶段尚无轮次", + "tournament_not_started_title": "锦标赛尚未开始", + "tournament_not_started_description": "请等待锦标赛开始。", + "edit_name_button": "编辑名称", + "delete_button": "删除", + "active_badge_label": "活跃", + "auto_create_matches_button": "自动添加新比赛", + "add_stage_button": "添加阶段", + "next_stage_button": "前往下一阶段", + "previous_stage_button": "前往上一阶段", + "multiple_teams_input_label": "添加多个队伍。每行放一个队伍", + "multiple_teams_input_placeholder": "队伍1", + "multiple_players_input_label": "添加多个玩家。每行放一个玩家", + "multiple_players_input_placeholder": "玩家1", + "edit_details_tab_title": "编辑详情", + "edit_password_tab_title": "编辑密码", + "no_team_members_description": "无成员", + "active_next_round_modal_title": "为下一轮比赛分配时间和场地", + "active_next_round_modal_description": "这将为下一轮比赛(即当前激活(绿色)轮次之后的轮次)分配时间和场地。", + "active_next_round_modal_choose_description": "你可以选择(勾选或不勾选复选框):", + "checkbox_status_checked": "已勾选", + "checkbox_status_unchecked": "未勾选", + "active_next_round_modal_choose_option_unchecked": "使用默认时间(下一场比赛将在当前轮次结束后紧接着计划,考虑到间隔时间)", + "active_next_round_modal_choose_option_checked": "调整下一场比赛的开始时间为立即开始(现在)。这将通过修改上一轮比赛的间隔时间来完成。", + "adjust_start_times_checkbox_label": "将本轮比赛的开始时间调整为当前时间", + "plan_next_round_button": "计划下一轮", + "edit_club_button": "编辑俱乐部", + "create_club_button": "创建俱乐部", + "club_name_input_placeholder": "最佳俱乐部", + "team_count_select_elimination_label": "从上一阶段晋级的队伍数量", + "team_count_select_elimination_placeholder": "2,4,8等", + "team_count_input_round_robin_label": "从上一阶段晋级的队伍数量", + "none": "无", + "at_least_two_team_validation": "需要至少两个队伍", + "stage_type_select_label": "阶段类型", + "round_robin_label": "循环赛", + "single_elimination_label": "单淘汰赛", + "swiss_label": "瑞士制", + "create_stage_item_button": "创建阶段项目", + "add_stage_item_modal_title": "添加阶段项目", + "remove_match_button": "移除比赛", + "negative_score_validation": "分数不能为负", + "negative_match_duration_validation": "比赛持续时间不能为负", + "negative_match_margin_validation": "比赛间隔时间不能为负", + "edit_match_modal_title": "编辑比赛", + "score_of_label": "得分", + "custom_match_duration_label": "自定义比赛持续时间", + "customize_checkbox_label": "自定义", + "custom_match_margin_label": "自定义比赛间隔时间", + "minutes": "分钟", + "at_least_one_player_validation": "输入至少一个玩家", + "active_players_checkbox_label": "这些玩家是活跃的", + "active_player_checkbox_label": "这个玩家是活跃的", + "player_name_input_placeholder": "最佳玩家", + "save_players_button": "保存玩家", + "single_player_title": "单个玩家", + "multiple_players_title": "多个玩家", + "create_player_modal_title": "创建玩家", + "add_player_button": "添加玩家", + "edit_player": "编辑玩家", + "delete_round_button": "删除轮次", + "edit_round": "编辑轮次", + "round_name_input_placeholder": "最佳轮次", + "active_round_checkbox_label": "这个轮次是活跃的", + "draft_round_checkbox_label": "这个轮次是草稿轮次", + "home_title": "主页", + "home_spotlight_description": "前往主页", + "clubs_spotlight_description": "查看、添加或删除俱乐部", + "user_settings_title": "用户设置", + "user_settings_spotlight_description": "更改名称、电子邮件、密码等", + "planning_title": "规划", + "planning_spotlight_description": "更改比赛规划", + "teams_title": "队伍", + "teams_spotlight_description": "查看、添加或删除队伍", + "players_title": "玩家", + "players_spotlight_description": "查看、添加或删除玩家", + "stage_title": "阶段", + "stage_spotlight_description": "更改锦标赛的布局", + "court_title": "场地", + "court_spotlight_description": "查看、添加或删除场地", + "tournament_setting_title": "锦标赛设置", + "tournament_setting_spotlight_description": "更改锦标赛的设置", + "nothing_found_placeholder": "未找到任何内容...", + "search_placeholder": "搜索...", + "at_least_one_team_validation": "输入至少一个队伍", + "active_teams_checkbox_label": "这些队伍是活跃的", + "team_name_input_placeholder": "最佳队伍", + "active_team_checkbox_label": "这个队伍是活跃的", + "team_member_select_placeholder": "选择你喜欢的所有成员", + "team_member_select_title": "队伍成员", + "single_team": "单个队伍", + "multiple_teams": "多个队伍", + "add_team_button": "添加队伍", + "edit_team_title": "编辑队伍", + "club_select_label": "俱乐部", + "now_button": "现在", + "logout_title": "登出", + "user_title": "用户", + "more_title": "更多", + "website_title": "网站", + "github_title": "Github", + "api_docs_title": "API 文档", + "elo_input_label": "最大 ELO 差值", + "only_recommended_input_group_label": "只显示比赛次数较少的队伍", + "only_recommended_radio_label": "只推荐", + "all_matches_radio_label": "所有比赛", + "max_results_input_label": "最大结果", + "iterations_input_label": "迭代", + "delete_club_button": "删除俱乐部", + "title": "标题", + "name_field_text": "名称", + "delete_court_button": "删除场地", + "win_distribution_text_win": "胜利", + "win_distribution_text_draws": "平局", + "win_distribution_text_losses": "失败", + "delete_player_button": "删除玩家", + "status": "状态", + "created": "已创建", + "elo_score": "ELO 分数", + "swiss_score": "瑞士分数", + "name_table_header": "名称", + "member_table_header": "成员", + "active": "活跃", + "inactive": "不活跃", + "delete_team_button": "删除队伍", + "delete_tournament_button": "删除锦标赛", + "edit_tournament_button": "编辑锦标赛", + "recommended_badge_title": "推荐", + "upcoming_matches_empty_table_info":"即将进行的比赛", + "elo_difference": "ELO 差值", + "swiss_difference": "瑞士差值", + "could_not_find_any_alert": "找不到任何", + "dropzone_accept_text": "在此处放置文件", + "dropzone_reject_text": "图片必须小于 10MB", + "dropzone_idle_text": "上传标志", + "upload_placeholder": "在此处放置文件以上传为锦标赛标志。", + "number_required": "包含数字", + "lowercase_required": "包含小写字母", + "uppercase_required": "包含大写字母", + "special_character_required": "包含特殊字符", + "8_characters_required": "至少有 8 个字符", + "create_tournament_button": "创建锦标赛" +} \ No newline at end of file diff --git a/frontend/src/components/brackets/brackets.tsx b/frontend/src/components/brackets/brackets.tsx index e9766bba0..67ee33fb0 100644 --- a/frontend/src/components/brackets/brackets.tsx +++ b/frontend/src/components/brackets/brackets.tsx @@ -1,6 +1,7 @@ import { Alert, Button, Container, Grid, Group, Skeleton } from '@mantine/core'; import { GoPlus } from '@react-icons/all-files/go/GoPlus'; import { IconAlertCircle } from '@tabler/icons-react'; +import { useTranslation } from 'next-i18next'; import React from 'react'; import { SWRResponse } from 'swr'; @@ -22,6 +23,7 @@ function getRoundsGridCols( readOnly: boolean, displaySettings: BracketDisplaySettings ) { + const { t } = useTranslation(); let rounds: React.JSX.Element[] | React.JSX.Element = stageItem.rounds .sort((r1: any, r2: any) => (r1.name > r2.name ? 1 : -1)) .map((round: RoundInterface) => ( @@ -39,8 +41,13 @@ function getRoundsGridCols( if (rounds.length < 1) { rounds = ( - } title="No rounds" color="blue" radius="lg"> - There are no rounds in this stage item yet + } + title={t('no_round_title')} + color="blue" + radius="lg" + > + {t('no_round_description')} ); } @@ -68,7 +75,7 @@ function getRoundsGridCols( await swrStagesResponse.mutate(); }} > - Add Round + {t('add_round_button')} )} {hideAddRoundButton ? null : ( @@ -88,32 +95,45 @@ function getRoundsGridCols( } function NoRoundsAlert({ readOnly }: { readOnly: boolean }) { + const { t } = useTranslation(); if (readOnly) { return ( - } title="No rounds found" color="blue" radius="lg"> - Please wait for the organiser to add them. + } + title={t('no_round_found_title')} + color="blue" + radius="lg" + > + {t('no_round_found_description')} ); } return ( - } title="No rounds found" color="blue" radius="lg"> - There are no rounds in this stage yet + } + title={t('no_round_found_title')} + color="blue" + radius="lg" + > + {t('no_round_found_in_stage_description')} ); } function NotStartedAlert() { + const { t } = useTranslation(); + return ( } - title="Tournament has not started yet" + title={t('tournament_not_started_title')} color="blue" radius="lg" > - Please wait for the tournament to start. + {t('tournament_not_started_description')} ); diff --git a/frontend/src/components/builder/builder.tsx b/frontend/src/components/builder/builder.tsx index 71236a0d9..b6b8efeed 100644 --- a/frontend/src/components/builder/builder.tsx +++ b/frontend/src/components/builder/builder.tsx @@ -1,6 +1,7 @@ import { ActionIcon, Badge, Card, Group, Menu, Stack, Text, rem } from '@mantine/core'; import { IconDots, IconPencil, IconTrash } from '@tabler/icons-react'; import assert from 'assert'; +import { useTranslation } from 'next-i18next'; import React, { useState } from 'react'; import { SWRResponse } from 'swr'; @@ -55,6 +56,7 @@ function StageItemRow({ stageItem: StageItemWithRounds; swrStagesResponse: SWRResponse; }) { + const { t } = useTranslation(); const [opened, setOpened] = useState(false); const stageItemsLookup = getStageItemLookup(swrStagesResponse); @@ -103,7 +105,7 @@ function StageItemRow({ setOpened(true); }} > - Edit name + {t('edit_name_button')} } @@ -113,7 +115,7 @@ function StageItemRow({ }} color="red" > - Delete + {t('delete_button')} @@ -133,6 +135,7 @@ function StageColumn({ stage: StageWithStageItems; swrStagesResponse: SWRResponse; }) { + const { t } = useTranslation(); const [opened, setOpened] = useState(false); const teamsMap = getTeamsLookup(tournament != null ? tournament.id : -1); @@ -166,7 +169,7 @@ function StageColumn({ {stage.name} {stage.is_active ? ( - Active + {t('active_badge_label')} ) : null} @@ -184,7 +187,7 @@ function StageColumn({ setOpened(true); }} > - Edit name + {t('edit_name_button')} } @@ -194,7 +197,7 @@ function StageColumn({ }} color="red" > - Delete + {t('delete_button')} diff --git a/frontend/src/components/buttons/create_matches_auto.tsx b/frontend/src/components/buttons/create_matches_auto.tsx index 65b78f42d..7b105e63c 100644 --- a/frontend/src/components/buttons/create_matches_auto.tsx +++ b/frontend/src/components/buttons/create_matches_auto.tsx @@ -1,5 +1,6 @@ import { Button } from '@mantine/core'; import { IconTool } from '@tabler/icons-react'; +import { useTranslation } from 'next-i18next'; import React from 'react'; import { SWRResponse } from 'swr'; @@ -20,6 +21,7 @@ export function AutoCreateMatchesButton({ swrStagesResponse: SWRResponse; swrUpcomingMatchesResponse: SWRResponse; }) { + const { t } = useTranslation(); if (roundId == null) { return null; } @@ -42,7 +44,7 @@ export function AutoCreateMatchesButton({ await swrUpcomingMatchesResponse.mutate(); }} > - Add new matches automatically + {t('auto_create_matches_button')} ); } diff --git a/frontend/src/components/buttons/create_stage.tsx b/frontend/src/components/buttons/create_stage.tsx index a2ecc09b3..d5a939141 100644 --- a/frontend/src/components/buttons/create_stage.tsx +++ b/frontend/src/components/buttons/create_stage.tsx @@ -1,5 +1,6 @@ import { Button } from '@mantine/core'; import { GoPlus } from '@react-icons/all-files/go/GoPlus'; +import { useTranslation } from 'next-i18next'; import React from 'react'; import { SWRResponse } from 'swr'; @@ -13,6 +14,8 @@ export default function CreateStageButton({ tournament: Tournament; swrStagesResponse: SWRResponse; }) { + const { t } = useTranslation(); + return ( ); } diff --git a/frontend/src/components/buttons/next_stage_button.tsx b/frontend/src/components/buttons/next_stage_button.tsx index 7c5131fc9..1434d8fe9 100644 --- a/frontend/src/components/buttons/next_stage_button.tsx +++ b/frontend/src/components/buttons/next_stage_button.tsx @@ -1,10 +1,12 @@ import { Button } from '@mantine/core'; import { IconSquareArrowLeft, IconSquareArrowRight } from '@tabler/icons-react'; +import { useTranslation } from 'next-i18next'; import React from 'react'; import { activateNextStage } from '../../services/stage'; export function NextStageButton({ tournamentData, swrStagesResponse }: any) { + const { t } = useTranslation(); return ( ); } export function PreviousStageButton({ tournamentData, swrStagesResponse }: any) { + const { t } = useTranslation(); + return ( ); } diff --git a/frontend/src/components/forms/player_create_csv_input.tsx b/frontend/src/components/forms/player_create_csv_input.tsx index 3647f0769..2dbcb4af0 100644 --- a/frontend/src/components/forms/player_create_csv_input.tsx +++ b/frontend/src/components/forms/player_create_csv_input.tsx @@ -1,12 +1,14 @@ import { Textarea } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; +import { useTranslation } from 'next-i18next'; import React from 'react'; export function MultiPlayersInput({ form }: { form: UseFormReturnType }) { + const { t } = useTranslation(); return (