Quellcode durchsuchen

Merge pull request #6510 from weseek/imprv/103511-admin-translation-zh

Imprv/103511 admin translation zh
Yuki Takei vor 3 Jahren
Ursprung
Commit
0029f93507
32 geänderte Dateien mit 422 neuen und 407 gelöschten Zeilen
  1. 26 4
      packages/app/public/static/locales/en_US/admin.json
  2. 0 22
      packages/app/public/static/locales/en_US/translation.json
  3. 2 2
      packages/app/public/static/locales/ja_JP/admin.json
  4. 275 1
      packages/app/public/static/locales/zh_CN/admin.json
  5. 0 261
      packages/app/public/static/locales/zh_CN/translation.json
  6. 2 2
      packages/app/src/components/Admin/Common/AdminNavigation.jsx
  7. 1 1
      packages/app/src/components/Admin/Common/AdminUpdateButtonRow.tsx
  8. 5 5
      packages/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx
  9. 7 7
      packages/app/src/components/Admin/Customize/CustomizeSidebarSetting.tsx
  10. 3 3
      packages/app/src/components/Admin/Customize/CustomizeThemeOptions.jsx
  11. 1 1
      packages/app/src/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.tsx
  12. 1 1
      packages/app/src/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx
  13. 2 2
      packages/app/src/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx
  14. 2 2
      packages/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  15. 2 2
      packages/app/src/components/Admin/ImportData/GrowiArchive/UploadForm.jsx
  16. 7 7
      packages/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx
  17. 21 21
      packages/app/src/components/Admin/ImportData/ImportDataPageContents.jsx
  18. 6 6
      packages/app/src/components/Admin/MarkdownSetting/IndentForm.tsx
  19. 7 7
      packages/app/src/components/Admin/MarkdownSetting/LineBreakForm.jsx
  20. 9 9
      packages/app/src/components/Admin/MarkdownSetting/MarkDownSettingContents.tsx
  21. 11 11
      packages/app/src/components/Admin/MarkdownSetting/PresentationForm.jsx
  22. 5 5
      packages/app/src/components/Admin/MarkdownSetting/WhiteListInput.jsx
  23. 9 9
      packages/app/src/components/Admin/MarkdownSetting/XssForm.jsx
  24. 2 2
      packages/app/src/components/Admin/UserGroup/UserGroupTable.tsx
  25. 1 1
      packages/app/src/components/Admin/UserManagement.jsx
  26. 2 2
      packages/app/src/components/Admin/Users/UserMenu.jsx
  27. 3 3
      packages/app/src/components/Admin/Users/UserTable.jsx
  28. 3 3
      packages/app/src/pages/admin/[[...path]].page.tsx
  29. 0 1
      packages/app/src/server/routes/apiv3/personal-setting.js
  30. 2 2
      packages/app/src/server/views/admin/importer.html
  31. 2 2
      packages/app/src/server/views/admin/markdown.html
  32. 3 0
      packages/app/src/stores/personal-settings.tsx

+ 26 - 4
packages/app/public/static/locales/en_US/admin.json

@@ -218,7 +218,6 @@
       "ABLCRule": "Rule"
     }
   },
-  "markdown_settings": "Markdown Settings",
   "notification_settings": {
     "notification_settings": "Notification Settings",
     "slack_incoming_configuration": "Slack Incoming Webhooks configuration",
@@ -270,9 +269,8 @@
     "toggle_notification": "Updated setting of {{path}}"
   },
   "customize": "Customize",
-  "ipmport_data": "Import Data",
+  "import_data": "Import Data",
   "export_archive_data": "Export Archive Data",
-  "full_text_search_management": "Full Text Search Management",
   "mailer_setup_required":"<a href='/admin/app'>Email settings</a> are required to send.",
   "admin_top": {
     "management_wiki": "Management Wiki",
@@ -379,7 +377,8 @@
     "use_env_var_if_empty": "If the value in the database is empty, the value of the environment variable <code>{{variable}}</code> is used.",
     "note_for_the_only_env_option": "The GCS Settings is limited by the value of environment variable.<br>To change this setting, please change to false or delete the value of the environment variable <code>{{env}}</code> ."
   },
-  "markdown_setting": {
+  "markdown_settings": {
+    "markdown_settings": "Markdown Settings",
     "lineBreak_header": "Line break setting",
     "lineBreak_desc": "You can change line break settings.",
     "lineBreak_options": {
@@ -808,6 +807,29 @@
       "force_update_parents_description": "Enable this option to force the addition of missing users to the ancestor groups if they exist after changing a parent group."
     }
   },
+  "full_text_search_management": {
+    "full_text_search_management": "Full Text Search Management",
+    "elasticsearch_management": "Elasticsearch management",
+    "connection_status": "Connection status",
+    "connection_status_label_unconfigured": "UNCONFIGURED",
+    "connection_status_label_connected": "CONNECTED",
+    "connection_status_label_disconnected": "DISCONNECTED",
+    "connection_status_label_erroroccured": "ERROR OCCURED ON SEARCH SERVICE",
+    "indices_status": "Indices Status",
+    "indices_status_label_normalized": "NORMALIZED",
+    "indices_status_label_unnormalized": "REBUILDING or BROKEN",
+    "indices_summary": "Indices summary",
+    "reconnect": "Reconnect",
+    "reconnect_button": "Try to reconnect",
+    "reconnect_description": "Click the button to try to reconnect to Elasticsearch.",
+    "normalize": "Normalize",
+    "normalize_button": "Normalize indices",
+    "normalize_description": "Click the button to repair broken indices.",
+    "rebuild": "Rebuild",
+    "rebuild_button": "Rebuild index",
+    "rebuild_description_1": "Click the button to rebuild index and add all page datas.",
+    "rebuild_description_2": "This may take a while."
+  },
   "audit_log_management": {
     "audit_log": "Audit Log",
     "audit_log_settings": "Audit Log Settings",

+ 0 - 22
packages/app/public/static/locales/en_US/translation.json

@@ -671,28 +671,6 @@
       "error_duplicate_pages_found": "Multiple pages with the same path name were found. Please rename or delete and try again."
     }
   },
-  "full_text_search_management": {
-    "elasticsearch_management": "Elasticsearch management",
-    "connection_status": "Connection status",
-    "connection_status_label_unconfigured": "UNCONFIGURED",
-    "connection_status_label_connected": "CONNECTED",
-    "connection_status_label_disconnected": "DISCONNECTED",
-    "connection_status_label_erroroccured": "ERROR OCCURED ON SEARCH SERVICE",
-    "indices_status": "Indices Status",
-    "indices_status_label_normalized": "NORMALIZED",
-    "indices_status_label_unnormalized": "REBUILDING or BROKEN",
-    "indices_summary": "Indices summary",
-    "reconnect": "Reconnect",
-    "reconnect_button": "Try to reconnect",
-    "reconnect_description": "Click the button to try to reconnect to Elasticsearch.",
-    "normalize": "Normalize",
-    "normalize_button": "Normalize indices",
-    "normalize_description": "Click the button to repair broken indices.",
-    "rebuild": "Rebuild",
-    "rebuild_button": "Rebuild index",
-    "rebuild_description_1": "Click the button to rebuild index and add all page datas.",
-    "rebuild_description_2": "This may take a while."
-  },
   "to_cloud_settings": "Open GROWI.cloud Settings",
   "login": {
     "Sign in error": "Login error",

+ 2 - 2
packages/app/public/static/locales/ja_JP/admin.json

@@ -218,7 +218,6 @@
       "ABLCRule": "ルール"
     }
   },
-  "markdown_settings": "マークダウン設定",
   "notification_settings": {
     "notification_settings": "通知設定",
     "slack_incoming_configuration": "Slack Incoming Webhooks 設定",
@@ -270,7 +269,7 @@
     "toggle_notification": "{{path}}の通知設定を変更しました"
   },
   "customize": "カスタマイズ",
-  "ipmport_data": "データインポート",
+  "import_data": "データインポート",
   "export_archive_data": "データアーカイブ",
   "full_text_search_management": {
     "full_text_search_management": "全文検索管理",
@@ -402,6 +401,7 @@
     "note_for_the_only_env_option": "現在GCS設定は環境変数の値によって制限されています<br>この設定を変更する場合は環境変数 <code>{{env}}</code> の値をfalseに変更もしくは削除してください"
   },
   "markdown_setting": {
+    "markdown_settings": "マークダウン設定",
     "lineBreak_header": "Line Break設定",
     "lineBreak_desc": "Line Breakの設定を変更できます。",
     "lineBreak_options": {

+ 275 - 1
packages/app/public/static/locales/zh_CN/admin.json

@@ -1,4 +1,260 @@
 {
+  "Update": "更新",
+  "Delete": "删除",
+  "User": "用户",
+  "Name": "姓名",
+  "Created": "创建",
+  "Edit": "编辑",
+  "Description": "描述",
+  "wiki_management_home_page": "Wiki管理首页",
+  "app_settings": "系统设置",
+  "public": "公共",
+  "anyone_with_the_link": "任何人",
+  "specified_users": "仅指定用户",
+  "only_me": "只有我",
+  "only_inside_the_group": "仅组内",
+  "security_settings": {
+    "security_settings": "安全设置",
+    "scope_of_page_disclosure": "页面公开范围",
+    "set_point": "设定值",
+    "always_displayed": "始终显示",
+    "always_hidden": "总是隐藏",
+    "displayed_or_hidden": "显示/隐藏",
+    "Guest Users Access": "来宾用户访问",
+		"Fixed by env var": "这是由env var<code>%s=%s</code>修复的。",
+		"Register limitation": "注册限制",
+		"Register limitation desc": "限制新用户注册",
+		"The whitelist of registration permission E-mail address": "注册许可电子邮件地址的白名单",
+		"users_without_account": "无法访问没有帐户的用户",
+		"example": "例子",
+		"restrict_emails": "您可以通过编写电子邮件域(以@开头)将电子邮件注册限制为wiki。",
+		"for_example": " 例如,如果要将注册限制为growi.org网站域,你可以写",
+		"in_this_case": ";在这种情况下,只有growi.org网站域将能够注册,所有其他用户将被拒绝。",
+		"insert_single": "请每行插入一个电子邮件地址。",
+    "page_list_and_search_results": "页面列表/搜索结果",
+		"page_listing_1": "页面列表/搜索<br>受“仅限我”限制",
+		"page_listing_1_desc": "列出/搜索时显示受“仅限我”选项限制的页面",
+		"page_listing_2": "页面列表/搜索<br>受用户组限制",
+		"page_listing_2_desc": "显示列出/搜索时受用户组限制的页面",
+    "page_access_rights": "页面访问",
+    "page_delete_rights": "删除权限",
+    "page_delete": "删除",
+    "page_delete_completely": "彻底删除",
+    "other_options": "其他选项",
+    "deletion_explain": "限制用户对选定的单一页面进行垃圾处理。",
+    "complete_deletion_explain": "限制可以完全删除所选单页的用户。",
+    "recursive_deletion_explain": "限制用户可以捣毁包括子孙在内的页面。",
+    "recursive_complete_deletion_explain": "限制可以完全删除页面的用户,包括子孙。",
+    "inherit": "继承(使用与单页相同的设置)。",
+		"admin_only": "仅管理员",
+		"admin_and_author": "管理员|作者",
+		"anyone": "任何人",
+    "session": "会议",
+    "max_age": "有效期间  (msec)",
+    "max_age_desc": "指定使用户会话过期的数量(以毫秒为单位)。<br>默认值: 2592000000 (30天)",
+    "max_age_caution": "修改该值后需要重启服务器。",
+    "forced_update_desc": "设置已被强行更改。以前的设置: ",
+    "page_delete_rights_caution": "\"删除/全部删除\"权限(包括后代页面)被强制强于\"删除/完全删除\"权限。 <br> <br> 仅管理员 > 管理员|作者 > 何人",
+		"Authentication mechanism settings": "身份验证机制设置",
+		"setup_is_not_yet_complete": "安装尚未完成",
+		"alert_siteUrl_is_not_set": "主页URL未设置,通过 {{link}} 设置",
+		"xss_prevent_setting": "阻止XSS(跨站点脚本)",
+		"xss_prevent_setting_link": "转到Markdown设置",
+		"callback_URL": "回调URL",
+		"providerName": "提供程序名称",
+		"issuerHost": "发行者主机",
+		"scope": "Scope",
+		"desc_of_callback_URL": "在{{AuthName}}身份提供程序的设置中使用它",
+    "authorization_endpoint": "Authorization Endpoint",
+    "token_endpoint": "Token Endpoint",
+    "revocation_endpoint": "Revocation Endpoint",
+    "introspection_endpoint": "Introspection Endpoint",
+    "userinfo_endpoint": "UserInfo Endpoint",
+    "end_session_endpoint": "EndSessioin Endpoint",
+    "registration_endpoint": "Registration Endpoint",
+    "jwks_uri": "JSON Web Key Set URL",
+		"clientID": "Client ID",
+		"client_secret": "客户机密",
+		"updated_general_security_setting": "更新安全设置成功",
+		"setup_not_completed_yet": "安装尚未完成",
+		"guest_mode": {
+			"deny": "拒绝(仅限注册用户)",
+			"readonly": "接受(来宾可以只读)"
+		},
+		"registration_mode": {
+			"open": "打开(任何人都可以注册)",
+			"restricted": "受限(需要管理员批准)",
+			"closed": "已关闭(仅限邀请)"
+		},
+    "share_link_rights": "分享链接权",
+    "enable_link_sharing": "启用链接共享",
+    "all_share_links": "所有共享链接",
+		"configuration": " 配置",
+		"optional": "可选的",
+		"Treat username matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>username</code> match",
+		"Treat username matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>username</code>.",
+		"Treat email matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>email</code> match",
+		"Treat email matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>email</code>.",
+		"Use env var if empty": "Use env var <code>{{env}}</code> if empty",
+		"Use default if both are empty": "If both ​​are empty, the default value <code>{{target}}</code> is used.",
+		"missing mandatory configs": "The following mandatory items are not set in either database nor environment variables.",
+		"Local": {
+			"name": "ID/Password",
+			"note for the only env option": "The LOCAL authentication is limited by the value of environment variable.<br>To change this setting, please change to false or delete the value of the environment variable <code>{{env}}</code> .",
+      "enable_local": "Enable ID/Password",
+      "password_reset_by_users": "用户重置密码",
+      "enable_password_reset_by_users": "启用用户重置密码",
+      "password_reset_desc": "忘记密码时,用户可以自行重置",
+      "email_authentication": "用户注册时的电子邮件身份验证",
+      "enable_email_authentication": "启用电子邮件身份验证",
+      "enable_email_authentication_desc": "用户注册将执行电子邮件身份验证。",
+      "please_enable_mailer": "请先设置邮件程序。",
+      "need_complete_mail_setting_warning": "要使用以下功能,请完成邮件设置。"
+		},
+		"ldap": {
+			"enable_ldap": "Enable LDAP",
+			"server_url_detail": "The LDAP URL of the directory service in the format <code>ldap://host:port/DN</code> or <code>ldaps://host:port/DN</code>.",
+			"bind_mode": "Binding Mode",
+			"bind_manager": "Manager Bind",
+			"bind_user": "User Bind",
+			"bind_DN_manager_detail": "The DN of the account that authenticates and queries the directory service",
+			"bind_DN_user_detail1": "The query used to bind with the directory service.",
+			"bind_DN_user_detail2": "Use <code>&#123;&#123;username&#125;&#125;</code> to reference the username entered in the login page.",
+			"bind_DN_password": "Bind DN Password",
+			"bind_DN_password_manager_detail": "The password for the Bind DN account.",
+			"bind_DN_password_user_detail": "The password that is entered in the login page will be used to bind.",
+			"search_filter": "Search Filter",
+			"search_filter_detail1": "The query used to locate the authenticated user.",
+			"search_filter_detail2": "Use <code>&#123;&#123;username&#125;&#125;</code> to reference the username entered in the login page.",
+			"search_filter_detail3": "If empty, the filter <code>(uid=&#123;&#123;username&#125;&#125;)</code> is used.",
+			"search_filter_example1": "Match with 'uid' or 'mail'",
+			"search_filter_example2": "Match with 'sAMAccountName' for Active Directory",
+			"username_detail": "Specification of mappings for <code>username</code> when creating new users",
+			"name_detail": "Specification of mappings for full name when creating new users",
+			"mail_detail": "Specification of mappings for mail address when creating new users",
+			"group_search_base_DN": "Group Search Base DN",
+			"group_search_base_DN_detail": "The base DN from which to search for groups. If defined, also <code>Group Search Filter</code> must be defined for the search to work.",
+			"group_search_filter": "Group Search Filter",
+			"group_search_filter_detail1": "The query used to filter for groups.",
+			"group_search_filter_detail2": "Login via LDAP is accepted only when this query hits one or more groups.",
+			"group_search_filter_detail3": "Use <code>&#123;&#123;dn&#125;&#125;</code> to have it replaced of the found user object.",
+			"group_search_filter_detail4": "<code>(&(cn=group1)(memberUid=&#123;&#123;dn&#125;&#125;))</code> hits the groups which has <code>cn=group1</code> and <code>memberUid</code> includes the user's <code>uid</code>(when <code>Group DN Property</code> is not changed from the default value.)",
+			"group_search_user_DN_property": "User DN Property",
+			"group_search_user_DN_property_detail": "The property of user object to use in <code>&#123;&#123;dn&#125;&#125;</code> interpolation of <code>Group Search Filter</code>.",
+			"test_config": "Test Saved Configuration",
+			"updated_ldap": "Succeeded to update LDAP setting"
+		},
+		"SAML": {
+			"name": "SAML",
+			"enable_saml": "Enable SAML",
+			"id_detail": "Specification of the name of attribute which can identify the user in SAML Identity Provider",
+			"username_detail": "Specification of mappings for <code>username</code> when creating new users",
+			"mapping_detail": "Specification of mappings for {{target}} when creating new users",
+			"cert_detail": "PEM-encoded X.509 signing certificate to validate the response from IdP",
+			"Use env var if empty": "If the value in the database is empty, the value of the environment variable <code>{{env}}</code> is used.",
+			"note for the only env option": "The setting item that enables or disables the SAML authentication and the highlighted setting items use only the value of environment variables.<br>To change this setting, please change to false or delete the value of the environment variable <code>{{env}}</code> .",
+			"attr_based_login_control_detail": "Limit who can sign up by using <code>&lt;saml: Attribute&gt;</code> element included in <code>&lt;saml: AttributeStatement&gt;</code> element and its child element <code>&lt;saml: AttributeValue&gt;</code>.",
+			"attr_based_login_control_rule_help": "<h5>Supported Queries:</h5><ul><li>Terms</li><li>Fields</li><li>AND/NOT/OR Operator</li><li>Grouping</li></ul><h5>Unsupported Queries:</h5><ul><li>Wildcard, Fuzzy, Proximity, Range and Boosting</li><li>+/- Operator</li><li>Field Grouping</li></ul><h5>Escaping special characters</h5>It is needed to escape following special characters:<br><code>+ - && || ! ( ) { } [ ] ^ &quot; &tilde; * ? : &#92;</code> and <code>/</code>",
+			"attr_based_login_control_rule_example1": "<h5>Example for conditions</h5>If a rule is <code>(Department: A || Department: B) && Position: Leader</code>, users who have either <code>Department: A</code> or <code>Department: B</code> and have <code>Position: Leader</code> <strong>can</strong> sign in.",
+      "attr_based_login_control_rule_example2": "<h5>Example for escaping</h5>If you would like to use URL as a query value, escape the following:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
+      "updated_saml": "Succeeded to update SAML setting"
+		},
+		"Basic": {
+			"enable_basic": "Enable Basic",
+			"name": "Basic Authentication",
+			"desc_1": "Login with <code>username</code> in Authorization header.",
+			"desc_2": "User will be automatically generated if not exist.",
+			"updated_basic": "Succeeded to update Basic setting"
+		},
+		"OAuth": {
+			"enable_oidc": "Enable OIDC",
+			"register": "Register for %s",
+			"change_redirect_url": "Enter <code>%s</code> <br>(where <code>%s</code> is your host name) for \"Authorized redirect URIs\".",
+			"Google": {
+				"enable_google": "Enable Google OAuth",
+				"name": "Google OAuth",
+				"register_1": "Access {{link}}",
+				"register_2": "Create Project if no projects exist",
+				"register_3": "Create Credentials &rightarrow; OAuth client ID &rightarrow; Select \"Web application\"",
+				"register_4": "Register your OAuth App with one of Authorized redirect URIs as <code>{{url}}</code>",
+				"register_5": "Copy and paste your ClientID and Client Secret above",
+				"updated_google": "Succeeded to update Google OAuth setting"
+			},
+			"Facebook": {
+				"name": "Facebook OAuth"
+			},
+			"Twitter": {
+				"enable_twitter": "Enable Twitter OAuth",
+				"name": "Twitter OAuth",
+				"register_1": "Access {{link}}",
+				"register_2": "Sign in Twitter",
+				"register_3": "Create Credentials &rightarrow; OAuth client ID &rightarrow; Select \"Web application\"",
+				"register_4": "Register your OAuth App with one of Authorized redirect URIs as <code>{{url}}</code>",
+				"register_5": "Copy and paste your ClientID and Client Secret above",
+				"updated_twitter": "Succeeded to update Twitter OAuth setting"
+			},
+			"GitHub": {
+				"enable_github": "Enable GitHub OAuth",
+				"name": "GitHub OAuth",
+				"register_1": "Access {{link}}",
+				"register_2": "Register your OAuth App with \"Authorization callback URL\" as <code>{{url}}</code>",
+				"register_3": "Copy and paste your ClientID and Client Secret above",
+				"updated_github": "Succeeded to update GitHub OAuth setting"
+			},
+			"OIDC": {
+				"name": "OpenID Connect",
+				"id_detail": "Specification of the name of attribute which can identify the user in OIDC claims",
+				"username_detail": "Specification of mappings for <code>username</code> when creating new users",
+				"name_detail": "Specification of mappings for <code>name</code> when creating new users",
+				"mapping_detail": "Specification of mappings for %s when creating new users",
+				"register_1": "Contant to OIDC IdP Administrator",
+				"register_2": "Register your OIDC App with \"Authorization callback URL\" as <code>%s</code>",
+				"register_3": "Copy and paste your ClientID and Client Secret above",
+				"updated_oidc": "Succeeded to update OpenID Connect",
+        "Use discovered URL if empty": "Use discovered URL from \"Issuer Host\" if empty"
+			},
+			"how_to": {
+				"google": "How to configure Google OAuth?",
+				"github": "How to configure GitHub OAuth?",
+				"twitter": "How to configure Twitter OAuth?",
+				"oidc": "How to configure OIDC?"
+			}
+		},
+		"form_item_name": {
+			"entryPoint": "Entry point",
+			"issuer": "Issuer",
+			"cert": "Certificate",
+			"attrMapId": "ID",
+			"attrMapUsername": "Username",
+			"attrMapMail": "Mail Address",
+			"attrMapFirstName": "First Name",
+			"attrMapLastName": "Last Name",
+			"ABLCRule": "Rule"
+		}
+  },
+  "full_text_search_management": {
+    "full_text_search_management": "全文搜索管理",
+		"elasticsearch_management": "Elasticsearch管理",
+		"connection_status": "连接状态",
+		"connection_status_label_unconfigured": "未配置",
+		"connection_status_label_connected": "已连接",
+		"connection_status_label_disconnected": "断开的",
+		"connection_status_label_erroroccured": "搜索服务出错",
+		"indices_status": "索引状态",
+		"indices_status_label_normalized": "标准化",
+		"indices_status_label_unnormalized": "重建或损坏",
+		"indices_summary": "索引摘要",
+		"reconnect": "重新连接",
+		"reconnect_button": "尝试重新连接",
+		"reconnect_description": "单击按钮尝试重新连接到Elasticsearch。",
+		"normalize": "规范化",
+		"normalize_button": "规范化索引",
+		"normalize_description": "单击按钮修复损坏的索引。",
+		"rebuild": "重建",
+		"rebuild_button": "重建索引",
+		"rebuild_description_1": "单击按钮以重新生成索引并添加所有页面数据。",
+		"rebuild_description_2": "这可能需要一段时间。"
+	},
   "mailer_setup_required": "<a href='/admin/app'>Email settings</a> are required to send.",
   "admin_top": {
     "management_wiki": "管理Wiki",
@@ -104,7 +360,8 @@
     "use_env_var_if_empty": "如果数据库中的值为空,则环境变量的值 <cod>{{variable}}</code> 启用。",
     "note_for_the_only_env_option": "The GCS settings is limited by the value of environment variable.<br>To change this setting, please change to false or delete the value of the environment variable <code>{{env}}</code> ."
   },
-  "markdown_setting": {
+  "markdown_settings": {
+    "markdown_settings": "Markdown设置",
     "lineBreak_header": "换行设置",
     "lineBreak_desc": "您可以更改换行设置。",
     "lineBreak_options": {
@@ -147,7 +404,9 @@
       "import_recommended": "导入建议 {{target}}"
     }
   },
+  "export_archive_data": "导出主题数据",
   "customize_setting": {
+    "customize_setting": "页面定制",
     "default_sidebar_mode": {
       "title": "默认的侧边栏模式",
       "desc": "你可以为新用户和访问该网页的客人设置侧边栏模式。",
@@ -226,6 +485,12 @@
     "delete_logo": "删除徽标"
   },
   "importer_management": {
+    "import_data": "导入数据",
+    "article": "主题",
+    "category": "分类",
+    "tag": "标签",
+    "page": "页面",
+    "page_path": "相对路径",
     "beta_warning": "这个函数是Beta。",
     "import_from": "Import from {{from}}",
     "import_growi_archive": "Import GROWI archive",
@@ -315,12 +580,14 @@
     "delete": "删除"
   },
   "external_notification": {
+    "external_notification": "外部通知",
     "enabled": "Enabled",
     "disabled": "Disabled",
     "header_status": "Slack整合状态",
     "caution_enabled": "CAUTION: 目前,在此页面中配置的通知只会通知设置为主要的 Slack 工作区。 "
   },
   "slack_integration": {
+    "slack_integration": "Slack一体化",
     "selecting_bot_types": {
       "slack_bot": "Slack bot",
       "detailed_explanation": "详细说明",
@@ -431,12 +698,15 @@
     }
   },
   "slack_integration_legacy": {
+    "slack_integration_legacy": "旧版Slack一体化",
     "alert_disabled": "由于<a href='/admin/slack-integration'>新设置</a>已启用,因此该'旧版Slack一体化'目前已被禁用。",
     "alert_deplicated": "这个 '旧版Slack一体化' 已经过时了,将来会停止使用。使用<a href='/admin/slack-integration'>新的设置</a>来代替。"
   },
   "user_management": {
+    "user_management": "用户管理",
     "invite_users": "临时发布新用户",
     "click_twice_same_checkbox": "您应该至少选中一个复选框。",
+    "status": "状态",
     "invite_modal": {
       "emails": "电子邮件",
       "description1": "通过电子邮件地址临时发布新用户。",
@@ -492,12 +762,14 @@
     "current_users": "当前用户:"
   },
   "user_group_management": {
+    "user_group_management": "用户组管理",
     "create_group": "创建新组",
     "add_child_group": "添加一个子组",
     "remove_child_group": "移除",
     "deny_create_group": "不能用当前设置创建新组。",
     "group_name": "组名",
     "group_example": "e.g.:第1组",
+    "child_user_group": "儿童用户组",
     "parent_group": "父母组",
     "select_parent_group": "选择父组",
     "release_parent_group": "Release parent group",
@@ -538,6 +810,8 @@
     }
   },
   "audit_log_management": {
+    "audit_log": "审计日志",
+    "audit_log_settings": "审计日志设置",
     "user": "用户",
     "username": "帐号",
     "date": "日期",

+ 0 - 261
packages/app/public/static/locales/zh_CN/translation.json

@@ -27,7 +27,6 @@
   "Description": "描述",
 	"Admin": "管理",
 	"administrator": "管理员",
-	"Tag": "标签",
 	"Tags": "Tags",
   "New": "新建",
   "Close": "Close",
@@ -36,12 +35,6 @@
 	"eg": "e.g.",
 	"add": "添加",
 	"Undo": "撤销",
-	"Article": "主题",
-	"Page": "页面",
-	"Page Path": "相对路径",
-	"Category": "分类",
-	"User": "用户",
-	"status": "状态",
 	"account_id": "用户Id",
 	"Initialize": "初始化",
   "Update": "更新",
@@ -122,28 +115,15 @@
 	"Input page name (optional)": "Input page name (optional)",
 	"New Page": "新页面",
 	"Create under": "Create page under below:",
-	"Wiki Management Home Page": "Wiki管理首页",
-	"App Settings": "系统设置",
   "V5 Page Migration": "转换为V5的兼容性",
   "GROWI.5.0_new_schema": "GROWI.5.0 new schema",
   "See_more_detail_on_new_schema": "更多详情请见<a href='#'>{{url}}</a> <i class='icon-share-alt'></i> ",
 	"Site URL settings": "主页URL设置",
 	"Markdown Settings": "Markdown设置",
-	"Customize": "页面定制",
 	"Notification Settings": "通知设置",
-  "slack_integration": "Slack一体化",
-  "External_Notification": "外部通知",
-  "Legacy_Slack_Integration": "旧版Slack一体化",
-	"User_Management": "用户管理",
 	"external_account_management": "外部账户管理",
   "UserGroup": "用户组",
   "ChildUserGroup": "儿童用户组",
-	"UserGroup Management": "用户组管理",
-  "AuditLog": "审计日志",
-  "AuditLog Settings": "审计日志设置",
-	"Full Text Search Management": "全文搜索管理",
-	"Import Data": "导入数据",
-	"Export Archive Data": "导出主题数据",
 	"Basic Settings": "基础设置",
 	"Basic authentication": "基本身份验证",
 	"Register limitation": "注册限制",
@@ -154,11 +134,6 @@
 	"Only me": "只有我",
   "Only inside the group": "仅组内",
   "page_list": "Page List",
-	"scope_of_page_disclosure": "页面公开范围",
-	"set_point": "设定值",
-	"always_displayed": "始终显示",
-	"always_hidden": "总是隐藏",
-	"displayed_or_hidden": "显示/隐藏",
 	"Reselect the group": "重新选择组",
 	"Shareable link": "可分享链接",
 	"The whitelist of registration permission E-mail address": "注册许可电子邮件地址的白名单",
@@ -256,7 +231,6 @@
 		"new_password_confirm": "重复新密码",
 		"password_is_not_set": "密码未设置"
 	},
-	"Security Settings": "安全设置",
 	"API Settings": "API设置",
 	"API Token Settings": "API token 设置",
 	"Current API Token": "当前 API token",
@@ -631,219 +605,6 @@
     "share_settings" :"Share settings",
     "Invalid_Number_of_Date" : "You entered invalid value"
   },
-	"security_setting": {
-		"Guest Users Access": "来宾用户访问",
-		"Fixed by env var": "这是由env var<code>%s=%s</code>修复的。",
-		"Register limitation": "注册限制",
-		"Register limitation desc": "限制新用户注册",
-		"The whitelist of registration permission E-mail address": "注册许可电子邮件地址的白名单",
-		"users_without_account": "无法访问没有帐户的用户",
-		"example": "例子",
-		"restrict_emails": "您可以通过编写电子邮件域(以@开头)将电子邮件注册限制为wiki。",
-		"for_example": " 例如,如果要将注册限制为growi.org网站域,你可以写",
-		"in_this_case": ";在这种情况下,只有growi.org网站域将能够注册,所有其他用户将被拒绝。",
-		"insert_single": "请每行插入一个电子邮件地址。",
-    "page_list_and_search_results": "页面列表/搜索结果",
-		"page_listing_1": "页面列表/搜索<br>受“仅限我”限制",
-		"page_listing_1_desc": "列出/搜索时显示受“仅限我”选项限制的页面",
-		"page_listing_2": "页面列表/搜索<br>受用户组限制",
-		"page_listing_2_desc": "显示列出/搜索时受用户组限制的页面",
-    "page_access_rights": "页面访问",
-    "page_delete_rights": "删除权限",
-    "page_delete": "删除",
-    "page_delete_completely": "彻底删除",
-    "other_options": "其他选项",
-    "deletion_explain": "限制用户对选定的单一页面进行垃圾处理。",
-    "complete_deletion_explain": "限制可以完全删除所选单页的用户。",
-    "recursive_deletion_explain": "限制用户可以捣毁包括子孙在内的页面。",
-    "recursive_complete_deletion_explain": "限制可以完全删除页面的用户,包括子孙。",
-    "inherit": "继承(使用与单页相同的设置)。",
-		"admin_only": "仅管理员",
-		"admin_and_author": "管理员|作者",
-		"anyone": "任何人",
-    "session": "会议",
-    "max_age": "有效期间  (msec)",
-    "max_age_desc": "指定使用户会话过期的数量(以毫秒为单位)。<br>默认值: 2592000000 (30天)",
-    "max_age_caution": "修改该值后需要重启服务器。",
-    "forced_update_desc": "设置已被强行更改。以前的设置: ",
-    "page_delete_rights_caution": "\"删除/全部删除\"权限(包括后代页面)被强制强于\"删除/完全删除\"权限。 <br> <br> 仅管理员 > 管理员|作者 > 何人",
-		"Authentication mechanism settings": "身份验证机制设置",
-		"setup_is_not_yet_complete": "安装尚未完成",
-		"alert_siteUrl_is_not_set": "主页URL未设置,通过 {{link}} 设置",
-		"xss_prevent_setting": "阻止XSS(跨站点脚本)",
-		"xss_prevent_setting_link": "转到Markdown设置",
-		"callback_URL": "回调URL",
-		"providerName": "提供程序名称",
-		"issuerHost": "发行者主机",
-		"scope": "Scope",
-		"desc_of_callback_URL": "在{{AuthName}}身份提供程序的设置中使用它",
-    "authorization_endpoint": "Authorization Endpoint",
-    "token_endpoint": "Token Endpoint",
-    "revocation_endpoint": "Revocation Endpoint",
-    "introspection_endpoint": "Introspection Endpoint",
-    "userinfo_endpoint": "UserInfo Endpoint",
-    "end_session_endpoint": "EndSessioin Endpoint",
-    "registration_endpoint": "Registration Endpoint",
-    "jwks_uri": "JSON Web Key Set URL",
-		"clientID": "Client ID",
-		"client_secret": "客户机密",
-		"updated_general_security_setting": "更新安全设置成功",
-		"setup_not_completed_yet": "安装尚未完成",
-		"guest_mode": {
-			"deny": "拒绝(仅限注册用户)",
-			"readonly": "接受(来宾可以只读)"
-		},
-		"registration_mode": {
-			"open": "打开(任何人都可以注册)",
-			"restricted": "受限(需要管理员批准)",
-			"closed": "已关闭(仅限邀请)"
-		},
-    "share_link_rights": "分享链接权",
-    "enable_link_sharing": "启用链接共享",
-    "all_share_links": "所有共享链接",
-		"configuration": " 配置",
-		"optional": "可选的",
-		"Treat username matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>username</code> match",
-		"Treat username matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>username</code>.",
-		"Treat email matching as identical": "Automatically bind external accounts newly logged in to local accounts when <code>email</code> match",
-		"Treat email matching as identical_warn": "WARNING: Be aware of security because the system treats the same user as a match of <code>email</code>.",
-		"Use env var if empty": "Use env var <code>{{env}}</code> if empty",
-		"Use default if both are empty": "If both ​​are empty, the default value <code>{{target}}</code> is used.",
-		"missing mandatory configs": "The following mandatory items are not set in either database nor environment variables.",
-		"Local": {
-			"name": "ID/Password",
-			"note for the only env option": "The LOCAL authentication is limited by the value of environment variable.<br>To change this setting, please change to false or delete the value of the environment variable <code>{{env}}</code> .",
-      "enable_local": "Enable ID/Password",
-      "password_reset_by_users": "用户重置密码",
-      "enable_password_reset_by_users": "启用用户重置密码",
-      "password_reset_desc": "忘记密码时,用户可以自行重置",
-      "email_authentication": "用户注册时的电子邮件身份验证",
-      "enable_email_authentication": "启用电子邮件身份验证",
-      "enable_email_authentication_desc": "用户注册将执行电子邮件身份验证。",
-      "please_enable_mailer": "请先设置邮件程序。",
-      "need_complete_mail_setting_warning": "要使用以下功能,请完成邮件设置。"
-		},
-		"ldap": {
-			"enable_ldap": "Enable LDAP",
-			"server_url_detail": "The LDAP URL of the directory service in the format <code>ldap://host:port/DN</code> or <code>ldaps://host:port/DN</code>.",
-			"bind_mode": "Binding Mode",
-			"bind_manager": "Manager Bind",
-			"bind_user": "User Bind",
-			"bind_DN_manager_detail": "The DN of the account that authenticates and queries the directory service",
-			"bind_DN_user_detail1": "The query used to bind with the directory service.",
-			"bind_DN_user_detail2": "Use <code>&#123;&#123;username&#125;&#125;</code> to reference the username entered in the login page.",
-			"bind_DN_password": "Bind DN Password",
-			"bind_DN_password_manager_detail": "The password for the Bind DN account.",
-			"bind_DN_password_user_detail": "The password that is entered in the login page will be used to bind.",
-			"search_filter": "Search Filter",
-			"search_filter_detail1": "The query used to locate the authenticated user.",
-			"search_filter_detail2": "Use <code>&#123;&#123;username&#125;&#125;</code> to reference the username entered in the login page.",
-			"search_filter_detail3": "If empty, the filter <code>(uid=&#123;&#123;username&#125;&#125;)</code> is used.",
-			"search_filter_example1": "Match with 'uid' or 'mail'",
-			"search_filter_example2": "Match with 'sAMAccountName' for Active Directory",
-			"username_detail": "Specification of mappings for <code>username</code> when creating new users",
-			"name_detail": "Specification of mappings for full name when creating new users",
-			"mail_detail": "Specification of mappings for mail address when creating new users",
-			"group_search_base_DN": "Group Search Base DN",
-			"group_search_base_DN_detail": "The base DN from which to search for groups. If defined, also <code>Group Search Filter</code> must be defined for the search to work.",
-			"group_search_filter": "Group Search Filter",
-			"group_search_filter_detail1": "The query used to filter for groups.",
-			"group_search_filter_detail2": "Login via LDAP is accepted only when this query hits one or more groups.",
-			"group_search_filter_detail3": "Use <code>&#123;&#123;dn&#125;&#125;</code> to have it replaced of the found user object.",
-			"group_search_filter_detail4": "<code>(&(cn=group1)(memberUid=&#123;&#123;dn&#125;&#125;))</code> hits the groups which has <code>cn=group1</code> and <code>memberUid</code> includes the user's <code>uid</code>(when <code>Group DN Property</code> is not changed from the default value.)",
-			"group_search_user_DN_property": "User DN Property",
-			"group_search_user_DN_property_detail": "The property of user object to use in <code>&#123;&#123;dn&#125;&#125;</code> interpolation of <code>Group Search Filter</code>.",
-			"test_config": "Test Saved Configuration",
-			"updated_ldap": "Succeeded to update LDAP setting"
-		},
-		"SAML": {
-			"name": "SAML",
-			"enable_saml": "Enable SAML",
-			"id_detail": "Specification of the name of attribute which can identify the user in SAML Identity Provider",
-			"username_detail": "Specification of mappings for <code>username</code> when creating new users",
-			"mapping_detail": "Specification of mappings for {{target}} when creating new users",
-			"cert_detail": "PEM-encoded X.509 signing certificate to validate the response from IdP",
-			"Use env var if empty": "If the value in the database is empty, the value of the environment variable <code>{{env}}</code> is used.",
-			"note for the only env option": "The setting item that enables or disables the SAML authentication and the highlighted setting items use only the value of environment variables.<br>To change this setting, please change to false or delete the value of the environment variable <code>{{env}}</code> .",
-			"attr_based_login_control_detail": "Limit who can sign up by using <code>&lt;saml: Attribute&gt;</code> element included in <code>&lt;saml: AttributeStatement&gt;</code> element and its child element <code>&lt;saml: AttributeValue&gt;</code>.",
-			"attr_based_login_control_rule_help": "<h5>Supported Queries:</h5><ul><li>Terms</li><li>Fields</li><li>AND/NOT/OR Operator</li><li>Grouping</li></ul><h5>Unsupported Queries:</h5><ul><li>Wildcard, Fuzzy, Proximity, Range and Boosting</li><li>+/- Operator</li><li>Field Grouping</li></ul><h5>Escaping special characters</h5>It is needed to escape following special characters:<br><code>+ - && || ! ( ) { } [ ] ^ &quot; &tilde; * ? : &#92;</code> and <code>/</code>",
-			"attr_based_login_control_rule_example1": "<h5>Example for conditions</h5>If a rule is <code>(Department: A || Department: B) && Position: Leader</code>, users who have either <code>Department: A</code> or <code>Department: B</code> and have <code>Position: Leader</code> <strong>can</strong> sign in.",
-      "attr_based_login_control_rule_example2": "<h5>Example for escaping</h5>If you would like to use URL as a query value, escape the following:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
-      "updated_saml": "Succeeded to update SAML setting"
-		},
-		"Basic": {
-			"enable_basic": "Enable Basic",
-			"name": "Basic Authentication",
-			"desc_1": "Login with <code>username</code> in Authorization header.",
-			"desc_2": "User will be automatically generated if not exist.",
-			"updated_basic": "Succeeded to update Basic setting"
-		},
-		"OAuth": {
-			"enable_oidc": "Enable OIDC",
-			"register": "Register for %s",
-			"change_redirect_url": "Enter <code>%s</code> <br>(where <code>%s</code> is your host name) for \"Authorized redirect URIs\".",
-			"Google": {
-				"enable_google": "Enable Google OAuth",
-				"name": "Google OAuth",
-				"register_1": "Access {{link}}",
-				"register_2": "Create Project if no projects exist",
-				"register_3": "Create Credentials &rightarrow; OAuth client ID &rightarrow; Select \"Web application\"",
-				"register_4": "Register your OAuth App with one of Authorized redirect URIs as <code>{{url}}</code>",
-				"register_5": "Copy and paste your ClientID and Client Secret above",
-				"updated_google": "Succeeded to update Google OAuth setting"
-			},
-			"Facebook": {
-				"name": "Facebook OAuth"
-			},
-			"Twitter": {
-				"enable_twitter": "Enable Twitter OAuth",
-				"name": "Twitter OAuth",
-				"register_1": "Access {{link}}",
-				"register_2": "Sign in Twitter",
-				"register_3": "Create Credentials &rightarrow; OAuth client ID &rightarrow; Select \"Web application\"",
-				"register_4": "Register your OAuth App with one of Authorized redirect URIs as <code>{{url}}</code>",
-				"register_5": "Copy and paste your ClientID and Client Secret above",
-				"updated_twitter": "Succeeded to update Twitter OAuth setting"
-			},
-			"GitHub": {
-				"enable_github": "Enable GitHub OAuth",
-				"name": "GitHub OAuth",
-				"register_1": "Access {{link}}",
-				"register_2": "Register your OAuth App with \"Authorization callback URL\" as <code>{{url}}</code>",
-				"register_3": "Copy and paste your ClientID and Client Secret above",
-				"updated_github": "Succeeded to update GitHub OAuth setting"
-			},
-			"OIDC": {
-				"name": "OpenID Connect",
-				"id_detail": "Specification of the name of attribute which can identify the user in OIDC claims",
-				"username_detail": "Specification of mappings for <code>username</code> when creating new users",
-				"name_detail": "Specification of mappings for <code>name</code> when creating new users",
-				"mapping_detail": "Specification of mappings for %s when creating new users",
-				"register_1": "Contant to OIDC IdP Administrator",
-				"register_2": "Register your OIDC App with \"Authorization callback URL\" as <code>%s</code>",
-				"register_3": "Copy and paste your ClientID and Client Secret above",
-				"updated_oidc": "Succeeded to update OpenID Connect",
-        "Use discovered URL if empty": "Use discovered URL from \"Issuer Host\" if empty"
-			},
-			"how_to": {
-				"google": "How to configure Google OAuth?",
-				"github": "How to configure GitHub OAuth?",
-				"twitter": "How to configure Twitter OAuth?",
-				"oidc": "How to configure OIDC?"
-			}
-		},
-		"form_item_name": {
-			"entryPoint": "Entry point",
-			"issuer": "Issuer",
-			"cert": "Certificate",
-			"attrMapId": "ID",
-			"attrMapUsername": "Username",
-			"attrMapMail": "Mail Address",
-			"attrMapFirstName": "First Name",
-			"attrMapLastName": "Last Name",
-			"ABLCRule": "Rule"
-		}
-	},
 	"notification_setting": {
 		"slack_incoming_configuration": "Slack Incoming Webhooks configuration",
 		"prioritize_webhook": "Prioritize incoming webhook than Slack App",
@@ -893,28 +654,6 @@
 		"delete_notification_pattern_desc2": "Once deleted, it cannot be recovered",
 		"toggle_notification": "Updated setting of {{path}}"
 	},
-	"full_text_search_management": {
-		"elasticsearch_management": "Elasticsearch管理",
-		"connection_status": "连接状态",
-		"connection_status_label_unconfigured": "未配置",
-		"connection_status_label_connected": "已连接",
-		"connection_status_label_disconnected": "断开的",
-		"connection_status_label_erroroccured": "搜索服务出错",
-		"indices_status": "索引状态",
-		"indices_status_label_normalized": "标准化",
-		"indices_status_label_unnormalized": "重建或损坏",
-		"indices_summary": "索引摘要",
-		"reconnect": "重新连接",
-		"reconnect_button": "尝试重新连接",
-		"reconnect_description": "单击按钮尝试重新连接到Elasticsearch。",
-		"normalize": "规范化",
-		"normalize_button": "规范化索引",
-		"normalize_description": "单击按钮修复损坏的索引。",
-		"rebuild": "重建",
-		"rebuild_button": "重建索引",
-		"rebuild_description_1": "单击按钮以重新生成索引并添加所有页面数据。",
-		"rebuild_description_2": "这可能需要一段时间。"
-	},
 	"personal_dropdown": {
 		"home": "家",
 		"settings": "设置",

+ 2 - 2
packages/app/src/components/Admin/Common/AdminNavigation.jsx

@@ -27,9 +27,9 @@ const AdminNavigation = (props) => {
     switch (menu) {
       case 'app':                      return <><i className="icon-fw icon-settings"></i>        { t('app_settings') }</>;
       case 'security':                 return <><i className="icon-fw icon-shield"></i>          { t('security_settings.security_settings') }</>;
-      case 'markdown':                 return <><i className="icon-fw icon-note"></i>            { t('markdown_settings') }</>;
+      case 'markdown':                 return <><i className="icon-fw icon-note"></i>            { t('markdown_settings.markdown_settings') }</>;
       case 'customize':                return <><i className="icon-fw icon-wrench"></i>          { t('customize') }</>;
-      case 'importer':                 return <><i className="icon-fw icon-cloud-upload"></i>    { t('ipmport_data') }</>;
+      case 'importer':                 return <><i className="icon-fw icon-cloud-upload"></i>    { t('importer_management.import_data') }</>;
       case 'export':                   return <><i className="icon-fw icon-cloud-download"></i>  { t('export_archive_data') }</>;
       case 'notification':             return <><i className="icon-fw icon-bell"></i>            { t('external_notification.external_notification')}</>;
       case 'slack-integration':        return <><i className="icon-fw icon-shuffle"></i>         { t('slack_integration.slack_integration') }</>;

+ 1 - 1
packages/app/src/components/Admin/Common/AdminUpdateButtonRow.tsx

@@ -9,7 +9,7 @@ type Props = {
 }
 
 const AdminUpdateButtonRow = (props: Props): JSX.Element => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return (
     <div className="row my-3">

+ 5 - 5
packages/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx

@@ -7,7 +7,7 @@ import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import { useNextThemes } from '~/stores/use-next-themes';
 
 const CustomizeLayoutSetting = (): JSX.Element => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   const { resolvedTheme } = useNextThemes();
 
@@ -32,7 +32,7 @@ const CustomizeLayoutSetting = (): JSX.Element => {
   const onClickSubmit = async() => {
     try {
       await apiv3Put('/customize-setting/layout', { isContainerFluid });
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.layout') }));
+      toastSuccess(t('toaster.update_successed', { target: t('customize_setting.layout') }));
       retrieveData();
     }
     catch (err) {
@@ -44,7 +44,7 @@ const CustomizeLayoutSetting = (): JSX.Element => {
     <React.Fragment>
       <div className="row">
         <div className="col-12">
-          <h2 className="admin-setting-header">{t('admin:customize_setting.layout')}</h2>
+          <h2 className="admin-setting-header">{t('customize_setting.layout')}</h2>
 
           <div className="d-flex justify-content-around mt-5">
             <div id="layoutOptions" className="card-deck">
@@ -55,7 +55,7 @@ const CustomizeLayoutSetting = (): JSX.Element => {
               >
                 <img src={`/images/customize-settings/default-${resolvedTheme}.svg`} />
                 <div className="card-body text-center">
-                  {t('admin:customize_setting.layout_options.default')}
+                  {t('customize_setting.layout_options.default')}
                 </div>
               </div>
               <div
@@ -65,7 +65,7 @@ const CustomizeLayoutSetting = (): JSX.Element => {
               >
                 <img src={`/images/customize-settings/fluid-${resolvedTheme}.svg`} />
                 <div className="card-body  text-center">
-                  {t('admin:customize_setting.layout_options.expanded')}
+                  {t('customize_setting.layout_options.expanded')}
                 </div>
               </div>
             </div>

+ 7 - 7
packages/app/src/components/Admin/Customize/CustomizeSidebarSetting.tsx

@@ -8,7 +8,7 @@ import { useSWRxSidebarConfig } from '~/stores/ui';
 import { useNextThemes } from '~/stores/use-next-themes';
 
 const CustomizeSidebarsetting = (): JSX.Element => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   const {
     update, isSidebarDrawerMode, isSidebarClosedAtDockMode, setIsSidebarDrawerMode, setIsSidebarClosedAtDockMode,
   } = useSWRxSidebarConfig();
@@ -20,7 +20,7 @@ const CustomizeSidebarsetting = (): JSX.Element => {
   const onClickSubmit = useCallback(async() => {
     try {
       await update();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.default_sidebar_mode.title') }));
+      toastSuccess(t('toaster.update_successed', { target: t('customize_setting.default_sidebar_mode.title') }));
     }
     catch (err) {
       toastError(err);
@@ -32,11 +32,11 @@ const CustomizeSidebarsetting = (): JSX.Element => {
       <div className="row">
         <div className="col-12">
 
-          <h2 className="admin-setting-header">{t('admin:customize_setting.default_sidebar_mode.title')}</h2>
+          <h2 className="admin-setting-header">{t('customize_setting.default_sidebar_mode.title')}</h2>
 
           <Card className="card well my-3">
             <CardBody className="px-0 py-2">
-              {t('admin:customize_setting.default_sidebar_mode.desc')}
+              {t('customize_setting.default_sidebar_mode.desc')}
             </CardBody>
           </Card>
 
@@ -67,7 +67,7 @@ const CustomizeSidebarsetting = (): JSX.Element => {
 
           <Card className="card well my-5">
             <CardBody className="px-0 py-2">
-              {t('admin:customize_setting.default_sidebar_mode.dock_mode_default_desc')}
+              {t('customize_setting.default_sidebar_mode.dock_mode_default_desc')}
             </CardBody>
           </Card>
 
@@ -83,7 +83,7 @@ const CustomizeSidebarsetting = (): JSX.Element => {
                 onChange={() => setIsSidebarClosedAtDockMode(false)}
               />
               <label className="custom-control-label" htmlFor="is-open">
-                {t('admin:customize_setting.default_sidebar_mode.dock_mode_default_open')}
+                {t('customize_setting.default_sidebar_mode.dock_mode_default_open')}
               </label>
             </div>
             <div className="custom-control custom-radio my-3">
@@ -97,7 +97,7 @@ const CustomizeSidebarsetting = (): JSX.Element => {
                 onChange={() => setIsSidebarClosedAtDockMode(true)}
               />
               <label className="custom-control-label" htmlFor="is-closed">
-                {t('admin:customize_setting.default_sidebar_mode.dock_mode_default_close')}
+                {t('customize_setting.default_sidebar_mode.dock_mode_default_close')}
               </label>
             </div>
           </div>

+ 3 - 3
packages/app/src/components/Admin/Customize/CustomizeThemeOptions.jsx

@@ -52,14 +52,14 @@ const CustomizeThemeOptions = (props) => {
   const { adminCustomizeContainer, currentTheme } = props;
   const { currentLayout } = adminCustomizeContainer.state;
 
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
 
   return (
     <div id="themeOptions" className={`${currentLayout === 'kibela' && 'disabled'}`}>
       {/* Light and Dark Themes */}
       <div>
-        <h3>{t('admin:customize_setting.theme_desc.light_and_dark')}</h3>
+        <h3>{t('customize_setting.theme_desc.light_and_dark')}</h3>
         <div className="d-flex flex-wrap">
           {lightNDarkTheme.map((theme) => {
             return (
@@ -75,7 +75,7 @@ const CustomizeThemeOptions = (props) => {
       </div>
       {/* Unique Theme */}
       <div className="mt-3">
-        <h3>{t('admin:customize_setting.theme_desc.unique')}</h3>
+        <h3>{t('customize_setting.theme_desc.unique')}</h3>
         <div className="d-flex flex-wrap">
           {uniqueTheme.map((theme) => {
             return (

+ 1 - 1
packages/app/src/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.tsx

@@ -9,7 +9,7 @@ type Props = {
 }
 
 const NormalizeIndicesControls = (props: Props): JSX.Element => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   const { isNormalized, isRebuildingProcessing } = props;
 
   const isEnabled = (isNormalized != null) && !isNormalized && !isRebuildingProcessing;

+ 1 - 1
packages/app/src/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx

@@ -108,7 +108,7 @@ class RebuildIndexControls extends React.Component {
 }
 
 const RebuildIndexControlsFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   const { data: socket } = useAdminSocket();
   return <RebuildIndexControls t={t} socket={socket} {...props} />;
 };

+ 2 - 2
packages/app/src/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx

@@ -2,8 +2,8 @@
 
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 import {
   Modal,
   ModalHeader,
@@ -233,7 +233,7 @@ ImportCollectionConfigurationModal.propTypes = {
 };
 
 const ImportCollectionConfigurationModalWrapperFc = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <ImportCollectionConfigurationModal t={t} {...props} />;
 };

+ 2 - 2
packages/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import AdminSocketIoContainer from '~/client/services/AdminSocketIoContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
@@ -505,7 +505,7 @@ ImportForm.propTypes = {
 };
 
 const ImportFormWrapperFc = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <ImportForm t={t} {...props} />;
 };

+ 2 - 2
packages/app/src/components/Admin/ImportData/GrowiArchive/UploadForm.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import { toastError } from '~/client/util/apiNotification';
 import { apiv3PostForm } from '~/client/util/apiv3-client';
@@ -98,7 +98,7 @@ UploadForm.propTypes = {
 };
 
 const UploadFormWrapperFc = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <UploadForm t={t} {...props} />;
 };

+ 7 - 7
packages/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx

@@ -95,7 +95,7 @@ class GrowiArchiveSection extends React.Component {
     const { t } = this.props;
     return (
       <div className="alert alert-warning mt-3">
-        {t('admin:importer_management.growi_settings.errors.different_versions')}
+        {t('importer_management.growi_settings.errors.different_versions')}
       </div>
     );
   }
@@ -110,17 +110,17 @@ class GrowiArchiveSection extends React.Component {
 
     return (
       <Fragment>
-        <h2>{t('admin:importer_management.import_growi_archive')}</h2>
+        <h2>{t('importer_management.import_growi_archive')}</h2>
         <div className="card well mb-4 small">
           <ul>
-            <li>{t('admin:importer_management.skip_username_and_email_when_overlapped')}</li>
-            <li>{t('admin:importer_management.prepare_new_account_for_migration')}</li>
+            <li>{t('importer_management.skip_username_and_email_when_overlapped')}</li>
+            <li>{t('importer_management.prepare_new_account_for_migration')}</li>
             <li>
               <a
-                href={`${t('admin:importer_management.admin_archive_data_import_guide_url')}`}
+                href={`${t('importer_management.admin_archive_data_import_guide_url')}`}
                 target="_blank"
                 rel="noopener noreferrer"
-              >{t('admin:importer_management.archive_data_import_detail')}
+              >{t('importer_management.archive_data_import_detail')}
               </a>
             </li>
           </ul>
@@ -154,7 +154,7 @@ GrowiArchiveSection.propTypes = {
 };
 
 const GrowiArchiveSectionWrapperFc = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <GrowiArchiveSection t={t} {...props} />;
 };

+ 21 - 21
packages/app/src/components/Admin/ImportData/ImportDataPageContents.jsx

@@ -29,7 +29,7 @@ class ImportDataPageContents extends React.Component {
           role="form"
         >
           <fieldset>
-            <h2 className="admin-setting-header">{t('admin:importer_management.import_from', { from: 'esa.io' })}</h2>
+            <h2 className="admin-setting-header">{t('importer_management.import_from', { from: 'esa.io' })}</h2>
             <table className="table table-bordered table-mapping">
               <thead>
                 <tr>
@@ -40,14 +40,14 @@ class ImportDataPageContents extends React.Component {
               </thead>
               <tbody>
                 <tr>
-                  <th>{t('Article')}</th>
+                  <th>{t('importer_management.article')}</th>
                   <th><i className="icon-arrow-right-circle text-success"></i></th>
-                  <th>{t('Page')}</th>
+                  <th>{t('importer_management.page')}</th>
                 </tr>
                 <tr>
-                  <th>{t('Category')}</th>
+                  <th>{t('importer_management.category')}</th>
                   <th><i className="icon-arrow-right-circle text-success"></i></th>
-                  <th>{t('Page Path')}</th>
+                  <th>{t('importer_management.page_path')}</th>
                 </tr>
                 <tr>
                   <th>{t('User')}</th>
@@ -59,7 +59,7 @@ class ImportDataPageContents extends React.Component {
 
             <div className="card well mb-0 small">
               <ul>
-                <li>{t('admin:importer_management.page_skip')}</li>
+                <li>{t('importer_management.page_skip')}</li>
               </ul>
             </div>
 
@@ -69,7 +69,7 @@ class ImportDataPageContents extends React.Component {
 
             <div className="form-group row">
               <label htmlFor="settingForm[importer:esa:team_name]" className="text-left text-md-right col-md-3 col-form-label">
-                {t('admin:importer_management.esa_settings.team_name')}
+                {t('importer_management.esa_settings.team_name')}
               </label>
               <div className="col-md-6">
                 <input
@@ -85,7 +85,7 @@ class ImportDataPageContents extends React.Component {
 
             <div className="form-group row">
               <label htmlFor="settingForm[importer:esa:access_token]" className="text-left text-md-right col-md-3 col-form-label">
-                {t('admin:importer_management.esa_settings.access_token')}
+                {t('importer_management.esa_settings.access_token')}
               </label>
               <div className="col-md-6">
                 <input
@@ -106,7 +106,7 @@ class ImportDataPageContents extends React.Component {
                   className="btn btn-primary btn-esa"
                   name="Esa"
                   onClick={adminImportContainer.esaHandleSubmit}
-                  value={t('admin:importer_management.import')}
+                  value={t('importer_management.import')}
                 />
                 <input type="button" className="btn btn-secondary" onClick={adminImportContainer.esaHandleSubmitUpdate} value={t('Update')} />
                 <span className="offset-0 offset-sm-1">
@@ -116,7 +116,7 @@ class ImportDataPageContents extends React.Component {
                     name="Esa"
                     className="btn btn-secondary btn-esa"
                     onClick={adminImportContainer.esaHandleSubmitTest}
-                    value={t('admin:importer_management.esa_settings.test_connection')}
+                    value={t('importer_management.esa_settings.test_connection')}
                   />
                 </span>
 
@@ -131,7 +131,7 @@ class ImportDataPageContents extends React.Component {
           role="form"
         >
           <fieldset>
-            <h2 className="admin-setting-header">{t('admin:importer_management.import_from', { from: 'Qiita:Team' })}</h2>
+            <h2 className="admin-setting-header">{t('importer_management.import_from', { from: 'Qiita:Team' })}</h2>
             <table className="table table-bordered table-mapping">
               <thead>
                 <tr>
@@ -142,17 +142,17 @@ class ImportDataPageContents extends React.Component {
               </thead>
               <tbody>
                 <tr>
-                  <th>{t('Article')}</th>
+                  <th>{t('importer_management.article')}</th>
                   <th><i className="icon-arrow-right-circle text-success"></i></th>
-                  <th>{t('Page')}</th>
+                  <th>{t('page')}</th>
                 </tr>
                 <tr>
-                  <th>{t('Tag')}</th>
+                  <th>{t('importer_management.tag')}</th>
                   <th></th>
                   <th>-</th>
                 </tr>
                 <tr>
-                  <th>{t('admin:importer_management.Directory_hierarchy_tag')}</th>
+                  <th>{t('importer_management.Directory_hierarchy_tag')}</th>
                   <th></th>
                   <th>(TBD)</th>
                 </tr>
@@ -165,7 +165,7 @@ class ImportDataPageContents extends React.Component {
             </table>
             <div className="card well mb-0 small">
               <ul>
-                <li>{t('admin:importer_management.page_skip')}</li>
+                <li>{t('importer_management.page_skip')}</li>
               </ul>
             </div>
 
@@ -174,7 +174,7 @@ class ImportDataPageContents extends React.Component {
             </div>
             <div className="form-group row">
               <label htmlFor="settingForm[importer:qiita:team_name]" className="text-left text-md-right col-md-3 col-form-label">
-                {t('admin:importer_management.qiita_settings.team_name')}
+                {t('importer_management.qiita_settings.team_name')}
               </label>
               <div className="col-md-6">
                 <input
@@ -189,7 +189,7 @@ class ImportDataPageContents extends React.Component {
 
             <div className="form-group row">
               <label htmlFor="settingForm[importer:qiita:access_token]" className="text-left text-md-right col-md-3 col-form-label">
-                {t('admin:importer_management.qiita_settings.access_token')}
+                {t('importer_management.qiita_settings.access_token')}
               </label>
               <div className="col-md-6">
                 <input
@@ -211,7 +211,7 @@ class ImportDataPageContents extends React.Component {
                   className="btn btn-primary btn-qiita"
                   name="Qiita"
                   onClick={adminImportContainer.qiitaHandleSubmit}
-                  value={t('admin:importer_management.import')}
+                  value={t('importer_management.import')}
                 />
                 <input type="button" className="btn btn-secondary" onClick={adminImportContainer.qiitaHandleSubmitUpdate} value={t('Update')} />
                 <span className="offset-0 offset-sm-1">
@@ -221,7 +221,7 @@ class ImportDataPageContents extends React.Component {
                     id="importFromQiita"
                     className="btn btn-secondary btn-qiita"
                     onClick={adminImportContainer.qiitaHandleSubmitTest}
-                    value={t('admin:importer_management.qiita_settings.test_connection')}
+                    value={t('importer_management.qiita_settings.test_connection')}
                   />
                 </span>
 
@@ -245,7 +245,7 @@ ImportDataPageContents.propTypes = {
 };
 
 const ImportDataPageContentsWrapperFc = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   const { adminImportContainer } = props;
 

+ 6 - 6
packages/app/src/components/Admin/MarkdownSetting/IndentForm.tsx

@@ -21,12 +21,12 @@ type Props = {
 }
 
 const IndentForm = (props: Props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   const onClickSubmit = useCallback(async(props) => {
     try {
       await props.adminMarkDownContainer.updateIndentSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:markdown_setting.indent_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.indent_header') }));
     }
     catch (err) {
       toastError(err);
@@ -41,7 +41,7 @@ const IndentForm = (props: Props) => {
     return (
       <div className="col">
         <div>
-          <label htmlFor="adminPreferredIndentSize">{t('admin:markdown_setting.indent_options.indentSize')}</label>
+          <label htmlFor="adminPreferredIndentSize">{t('markdown_settings.indent_options.indentSize')}</label>
           <UncontrolledDropdown id="adminPreferredIndentSize">
             <DropdownToggle caret className="col-3 col-sm-2 col-md-5 col-lg-5 col-xl-3 text-right">
               <span className="float-left">
@@ -60,7 +60,7 @@ const IndentForm = (props: Props) => {
           </UncontrolledDropdown>
         </div>
         <p className="form-text text-muted">
-          {t('admin:markdown_setting.indent_options.indentSize_desc')}
+          {t('markdown_settings.indent_options.indentSize_desc')}
         </p>
       </div>
     );
@@ -70,7 +70,7 @@ const IndentForm = (props: Props) => {
     const { adminMarkDownContainer } = props;
     const { isIndentSizeForced } = adminMarkDownContainer.state;
 
-    const helpIndentInComment = { __html: t('admin:markdown_setting.indent_options.disallow_indent_change_desc') };
+    const helpIndentInComment = { __html: t('markdown_settings.indent_options.disallow_indent_change_desc') };
 
     return (
       <div className="col">
@@ -85,7 +85,7 @@ const IndentForm = (props: Props) => {
             }}
           />
           <label className="custom-control-label" htmlFor="isIndentSizeForced">
-            {t('admin:markdown_setting.indent_options.disallow_indent_change')}
+            {t('markdown_settings.indent_options.disallow_indent_change')}
           </label>
         </div>
         <p className="form-text text-muted" dangerouslySetInnerHTML={helpIndentInComment} />

+ 7 - 7
packages/app/src/components/Admin/MarkdownSetting/LineBreakForm.jsx

@@ -1,8 +1,8 @@
 /* eslint-disable react/no-danger */
 import React from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import AdminMarkDownContainer from '~/client/services/AdminMarkDownContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
@@ -27,7 +27,7 @@ class LineBreakForm extends React.Component {
 
     try {
       await this.props.adminMarkDownContainer.updateLineBreakSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:markdown_setting.lineBreak_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.lineBreak_header') }));
     }
     catch (err) {
       toastError(err);
@@ -39,7 +39,7 @@ class LineBreakForm extends React.Component {
     const { t, adminMarkDownContainer } = this.props;
     const { isEnabledLinebreaks } = adminMarkDownContainer.state;
 
-    const helpLineBreak = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_desc') };
+    const helpLineBreak = { __html: t('markdown_settings.lineBreak_options.enable_lineBreak_desc') };
 
     return (
       <div className="col">
@@ -52,7 +52,7 @@ class LineBreakForm extends React.Component {
             onChange={() => { adminMarkDownContainer.setState({ isEnabledLinebreaks: !isEnabledLinebreaks }) }}
           />
           <label className="custom-control-label" htmlFor="isEnabledLinebreaks">
-            {t('admin:markdown_setting.lineBreak_options.enable_lineBreak') }
+            {t('markdown_settings.lineBreak_options.enable_lineBreak') }
           </label>
         </div>
         <p className="form-text text-muted" dangerouslySetInnerHTML={helpLineBreak} />
@@ -64,7 +64,7 @@ class LineBreakForm extends React.Component {
     const { t, adminMarkDownContainer } = this.props;
     const { isEnabledLinebreaksInComments } = adminMarkDownContainer.state;
 
-    const helpLineBreakInComment = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_for_comment_desc') };
+    const helpLineBreakInComment = { __html: t('markdown_settings.lineBreak_options.enable_lineBreak_for_comment_desc') };
 
     return (
       <div className="col">
@@ -77,7 +77,7 @@ class LineBreakForm extends React.Component {
             onChange={() => { adminMarkDownContainer.setState({ isEnabledLinebreaksInComments: !isEnabledLinebreaksInComments }) }}
           />
           <label className="custom-control-label" htmlFor="isEnabledLinebreaksInComments">
-            {t('admin:markdown_setting.lineBreak_options.enable_lineBreak_for_comment') }
+            {t('markdown_settings.lineBreak_options.enable_lineBreak_for_comment') }
           </label>
         </div>
         <p className="form-text text-muted" dangerouslySetInnerHTML={helpLineBreakInComment} />
@@ -102,7 +102,7 @@ class LineBreakForm extends React.Component {
 }
 
 const LineBreakFormFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   return <LineBreakForm t={t} {...props} />;
 };
 

+ 9 - 9
packages/app/src/components/Admin/MarkdownSetting/MarkDownSettingContents.tsx

@@ -22,7 +22,7 @@ type Props ={
 }
 
 const MarkDownSettingContents = React.memo((props: Props): JSX.Element => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   const { adminMarkDownContainer } = props;
 
   useEffect(() => {
@@ -43,30 +43,30 @@ const MarkDownSettingContents = React.memo((props: Props): JSX.Element => {
   return (
     <div data-testid="admin-markdown">
       {/* Line Break Setting */}
-      <h2 className="admin-setting-header">{t('admin:markdown_setting.lineBreak_header')}</h2>
+      <h2 className="admin-setting-header">{t('markdown_settings.lineBreak_header')}</h2>
       <Card className="card well my-3">
-        <CardBody className="px-0 py-2">{ t('admin:markdown_setting.lineBreak_desc') }</CardBody>
+        <CardBody className="px-0 py-2">{ t('markdown_settings.lineBreak_desc') }</CardBody>
       </Card>
       <LineBreakForm />
 
       {/* Indent Setting */}
-      <h2 className="admin-setting-header">{t('admin:markdown_setting.indent_header')}</h2>
+      <h2 className="admin-setting-header">{t('markdown_settings.indent_header')}</h2>
       <Card className="card well my-3">
-        <CardBody className="px-0 py-2">{t('admin:markdown_setting.indent_desc') }</CardBody>
+        <CardBody className="px-0 py-2">{t('markdown_settings.indent_desc') }</CardBody>
       </Card>
       <IndentForm />
 
       {/* Presentation Setting */}
-      <h2 className="admin-setting-header">{ t('admin:markdown_setting.presentation_header') }</h2>
+      <h2 className="admin-setting-header">{ t('markdown_settings.presentation_header') }</h2>
       <Card className="card well my-3">
-        <CardBody className="px-0 py-2">{ t('admin:markdown_setting.presentation_desc') }</CardBody>
+        <CardBody className="px-0 py-2">{ t('markdown_settings.presentation_desc') }</CardBody>
       </Card>
       <PresentationForm />
 
       {/* XSS Setting */}
-      <h2 className="admin-setting-header">{ t('admin:markdown_setting.xss_header') }</h2>
+      <h2 className="admin-setting-header">{ t('markdown_settings.xss_header') }</h2>
       <Card className="card well my-3">
-        <CardBody className="px-0 py-2">{ t('admin:markdown_setting.xss_desc') }</CardBody>
+        <CardBody className="px-0 py-2">{ t('markdown_settings.xss_desc') }</CardBody>
       </Card>
       <XssForm />
     </div>

+ 11 - 11
packages/app/src/components/Admin/MarkdownSetting/PresentationForm.jsx

@@ -25,7 +25,7 @@ class PresentationForm extends React.Component {
 
     try {
       await this.props.adminMarkDownContainer.updatePresentationSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:markdown_setting.presentation_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.presentation_header') }));
     }
     catch (err) {
       toastError(err);
@@ -42,7 +42,7 @@ class PresentationForm extends React.Component {
       <fieldset className="form-group col-12 my-2">
 
         <label className="col-8 offset-4 col-form-label font-weight-bold text-left mt-3">
-          {t('admin:markdown_setting.presentation_options.page_break_setting')}
+          {t('markdown_settings.presentation_options.page_break_setting')}
         </label>
 
         <div className="form-group col-12 my-3">
@@ -57,13 +57,13 @@ class PresentationForm extends React.Component {
                   onChange={() => adminMarkDownContainer.switchPageBreakSeparator(1)}
                 />
                 <label className="custom-control-label w-100" htmlFor="pageBreakOption1">
-                  <p className="font-weight-bold">{ t('admin:markdown_setting.presentation_options.preset_one_separator') }</p>
+                  <p className="font-weight-bold">{ t('markdown_settings.presentation_options.preset_one_separator') }</p>
                   <div className="mt-3">
-                    { t('admin:markdown_setting.presentation_options.preset_one_separator_desc') }
+                    { t('markdown_settings.presentation_options.preset_one_separator_desc') }
                     <input
                       className="form-control"
                       type="text"
-                      value={t('admin:markdown_setting.presentation_options.preset_one_separator_value')}
+                      value={t('markdown_settings.presentation_options.preset_one_separator_value')}
                       readOnly
                     />
                   </div>
@@ -81,13 +81,13 @@ class PresentationForm extends React.Component {
                   onChange={() => adminMarkDownContainer.switchPageBreakSeparator(2)}
                 />
                 <label className="custom-control-label w-100" htmlFor="pageBreakOption2">
-                  <p className="font-weight-bold">{ t('admin:markdown_setting.presentation_options.preset_two_separator') }</p>
+                  <p className="font-weight-bold">{ t('markdown_settings.presentation_options.preset_two_separator') }</p>
                   <div className="mt-3">
-                    { t('admin:markdown_setting.presentation_options.preset_two_separator_desc') }
+                    { t('markdown_settings.presentation_options.preset_two_separator_desc') }
                     <input
                       className="form-control"
                       type="text"
-                      value={t('admin:markdown_setting.presentation_options.preset_two_separator_value')}
+                      value={t('markdown_settings.presentation_options.preset_two_separator_value')}
                       readOnly
                     />
                   </div>
@@ -104,9 +104,9 @@ class PresentationForm extends React.Component {
                   onChange={() => adminMarkDownContainer.switchPageBreakSeparator(3)}
                 />
                 <label className="custom-control-label w-100" htmlFor="pageBreakOption3">
-                  <p className="font-weight-bold">{ t('admin:markdown_setting.presentation_options.custom_separator') }</p>
+                  <p className="font-weight-bold">{ t('markdown_settings.presentation_options.custom_separator') }</p>
                   <div className="mt-3">
-                    { t('admin:markdown_setting.presentation_options.custom_separator_desc') }
+                    { t('markdown_settings.presentation_options.custom_separator_desc') }
                     <input
                       className="form-control"
                       defaultValue={pageBreakCustomSeparator}
@@ -133,7 +133,7 @@ PresentationForm.propTypes = {
 };
 
 const PresentationFormWrapperFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <PresentationForm t={t} {...props} />;
 };

+ 5 - 5
packages/app/src/components/Admin/MarkdownSetting/WhiteListInput.jsx

@@ -37,9 +37,9 @@ class WhiteListInput extends React.Component {
       <>
         <div className="mt-4">
           <div className="d-flex justify-content-between">
-            {t('admin:markdown_setting.xss_options.tag_names')}
+            {t('markdown_settings.xss_options.tag_names')}
             <p id="btn-import-tags" className="btn btn-sm btn-primary mb-0" onClick={this.onClickRecommendTagButton}>
-              {t('admin:markdown_setting.xss_options.import_recommended', { target: 'Tags' })}
+              {t('markdown_settings.xss_options.import_recommended', { target: 'Tags' })}
             </p>
           </div>
           <textarea
@@ -54,9 +54,9 @@ class WhiteListInput extends React.Component {
         </div>
         <div className="mt-4">
           <div className="d-flex justify-content-between">
-            {t('admin:markdown_setting.xss_options.tag_attributes')}
+            {t('markdown_settings.xss_options.tag_attributes')}
             <p id="btn-import-tags" className="btn btn-sm btn-primary mb-0" onClick={this.onClickRecommendAttrButton}>
-              {t('admin:markdown_setting.xss_options.import_recommended', { target: 'Attrs' })}
+              {t('markdown_settings.xss_options.import_recommended', { target: 'Attrs' })}
             </p>
           </div>
           <textarea
@@ -83,7 +83,7 @@ WhiteListInput.propTypes = {
 };
 
 const PresentationFormWrapperFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <WhiteListInput t={t} {...props} />;
 };

+ 9 - 9
packages/app/src/components/Admin/MarkdownSetting/XssForm.jsx

@@ -28,7 +28,7 @@ class XssForm extends React.Component {
 
     try {
       await this.props.adminMarkDownContainer.updateXssSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:markdown_setting.xss_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.xss_header') }));
     }
     catch (err) {
       toastError(err);
@@ -54,9 +54,9 @@ class XssForm extends React.Component {
                 onChange={() => { adminMarkDownContainer.setState({ xssOption: 1 }) }}
               />
               <label className="custom-control-label w-100" htmlFor="xssOption1">
-                <p className="font-weight-bold">{t('admin:markdown_setting.xss_options.remove_all_tags')}</p>
+                <p className="font-weight-bold">{t('markdown_settings.xss_options.remove_all_tags')}</p>
                 <div className="mt-4">
-                  {t('admin:markdown_setting.xss_options.remove_all_tags_desc')}
+                  {t('markdown_settings.xss_options.remove_all_tags_desc')}
                 </div>
               </label>
             </div>
@@ -73,10 +73,10 @@ class XssForm extends React.Component {
                 onChange={() => { adminMarkDownContainer.setState({ xssOption: 2 }) }}
               />
               <label className="custom-control-label w-100" htmlFor="xssOption2">
-                <p className="font-weight-bold">{t('admin:markdown_setting.xss_options.recommended_setting')}</p>
+                <p className="font-weight-bold">{t('markdown_settings.xss_options.recommended_setting')}</p>
                 <div className="mt-4">
                   <div className="d-flex justify-content-between">
-                    {t('admin:markdown_setting.xss_options.tag_names')}
+                    {t('markdown_settings.xss_options.tag_names')}
                   </div>
                   <textarea
                     className="form-control xss-list"
@@ -89,7 +89,7 @@ class XssForm extends React.Component {
                 </div>
                 <div className="mt-4">
                   <div className="d-flex justify-content-between">
-                    {t('admin:markdown_setting.xss_options.tag_attributes')}
+                    {t('markdown_settings.xss_options.tag_attributes')}
                   </div>
                   <textarea
                     className="form-control xss-list"
@@ -115,7 +115,7 @@ class XssForm extends React.Component {
                 onChange={() => { adminMarkDownContainer.setState({ xssOption: 3 }) }}
               />
               <label className="custom-control-label w-100" htmlFor="xssOption3">
-                <p className="font-weight-bold">{t('admin:markdown_setting.xss_options.custom_whitelist')}</p>
+                <p className="font-weight-bold">{t('markdown_settings.xss_options.custom_whitelist')}</p>
                 <WhiteListInput customizable />
               </label>
             </div>
@@ -144,7 +144,7 @@ class XssForm extends React.Component {
                   onChange={adminMarkDownContainer.switchEnableXss}
                 />
                 <label className="custom-control-label w-100" htmlFor="XssEnable">
-                  {t('admin:markdown_setting.xss_options.enable_xss_prevention')}
+                  {t('markdown_settings.xss_options.enable_xss_prevention')}
                 </label>
               </div>
             </div>
@@ -168,7 +168,7 @@ XssForm.propTypes = {
 };
 
 const XssFormWrapperFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   return <XssForm t={t} {...props} />;
 };

+ 2 - 2
packages/app/src/components/Admin/UserGroup/UserGroupTable.tsx

@@ -54,7 +54,7 @@ const generateGroupIdToChildGroupsMap = (childUserGroups: IUserGroupHasId[]): Re
 
 
 export const UserGroupTable: FC<Props> = (props: Props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
 
   /*
    * State
@@ -135,7 +135,7 @@ export const UserGroupTable: FC<Props> = (props: Props) => {
             <th>{t('Name')}</th>
             <th>{t('Description')}</th>
             <th>{t('User')}</th>
-            <th>{t('ChildUserGroup')}</th>
+            <th>{t('user_group_management.child_user_group')}</th>
             <th style={{ width: 100 }}>{t('Created')}</th>
             <th style={{ width: 70 }}></th>
           </tr>

+ 1 - 1
packages/app/src/components/Admin/UserManagement.jsx

@@ -224,7 +224,7 @@ UserManagement.propTypes = {
 };
 
 const UserManagementFc = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   return <UserManagement t={t} {...props} />;
 };
 

+ 2 - 2
packages/app/src/components/Admin/Users/UserMenu.jsx

@@ -62,7 +62,7 @@ class UserMenu extends React.Component {
     return (
       <Fragment>
         <li className="dropdown-divider"></li>
-        <li className="dropdown-header">{t('status')}</li>
+        <li className="dropdown-header">{t('user_management.status')}</li>
         <li>
           {(user.status === 1 || user.status === 3) && <StatusActivateButton user={user} />}
           {user.status === 2 && <StatusSuspendedMenuItem user={user} />}
@@ -116,7 +116,7 @@ class UserMenu extends React.Component {
 }
 
 const UserMenuWrapperFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   return <UserMenu t={t} {...props} />;
 };
 

+ 3 - 3
packages/app/src/components/Admin/Users/UserTable.jsx

@@ -2,8 +2,8 @@ import React, { Fragment } from 'react';
 
 import { UserPicture } from '@growi/ui';
 import dateFnsFormat from 'date-fns/format';
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 
@@ -99,7 +99,7 @@ class UserTable extends React.Component {
                 <th>
                   <div className="d-flex align-items-center">
                     <div className="mr-3">
-                      {t('status')}
+                      {t('user_management.status')}
                     </div>
                     <SortIcons
                       isSelected={adminUsersContainer.state.sort === 'status'}
@@ -222,7 +222,7 @@ UserTable.propTypes = {
 };
 
 const UserTableWrapperFC = (props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   return <UserTable t={t} {...props} />;
 };
 

+ 3 - 3
packages/app/src/pages/admin/[[...path]].page.tsx

@@ -120,15 +120,15 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
       component: <SecurityManagementContents />,
     },
     markdown: {
-      title: t('markdown_settings'),
+      title: t('markdown_settings.markdown_settings'),
       component: <MarkDownSettingContents />,
     },
     customize: {
-      title: t('Customize Settings'),
+      title: t('customize_setting.customize_setting'),
       component: <CustomizeSettingContents />,
     },
     importer: {
-      title: t('Import Data'),
+      title: t('importer_management.import_data'),
       component: <DataImportPageContents />,
     },
     export: {

+ 0 - 1
packages/app/src/server/routes/apiv3/personal-setting.js

@@ -242,7 +242,6 @@ module.exports = (crowi) => {
       user.slackMemberId = req.body.slackMemberId;
 
       const updatedUser = await user.save();
-      req.i18n.changeLanguage(req.body.lang);
 
       const parameters = { action: SupportedAction.ACTION_USER_PERSONAL_SETTINGS_UPDATE };
       activityEvent.emit('update', res.locals.activity._id, parameters);

+ 2 - 2
packages/app/src/server/views/admin/importer.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('ipmport_data')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('importer_management.import_data')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('ipmport_data') }}</h1>
+<h1 class="title">{{ t('importer_management.import_data') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 2 - 2
packages/app/src/server/views/admin/markdown.html

@@ -1,9 +1,9 @@
 {% extends '../layout/admin.html' %}
 
-{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('markdown_settings')) }}{% endblock %}
+{% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('markdown_settings.markdown_settings')) }}{% endblock %}
 
 {% block content_header %}
-<h1 class="title">{{ t('markdown_settings') }}</h1>
+<h1 class="title">{{ t('markdown_settings.markdown_settings') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 3 - 0
packages/app/src/stores/personal-settings.tsx

@@ -1,3 +1,4 @@
+import { useTranslation } from 'next-i18next';
 import useSWR, { SWRResponse } from 'swr';
 
 
@@ -27,6 +28,7 @@ export type IPersonalSettingsInfoOption = {
 }
 
 export const usePersonalSettings = (): SWRResponse<IUser, Error> & IPersonalSettingsInfoOption => {
+  const { i18n } = useTranslation();
   const { data: personalSettingsDataFromDB, mutate: revalidate } = useSWRxPersonalSettings();
   const key = personalSettingsDataFromDB != null ? 'personalSettingsInfo' : null;
 
@@ -57,6 +59,7 @@ export const usePersonalSettings = (): SWRResponse<IUser, Error> & IPersonalSett
     // invoke API
     try {
       await apiv3Put('/personal-setting/', updateData);
+      i18n.changeLanguage(updateData.lang);
     }
     catch (err) {
       logger.error(err);