Просмотр исходного кода

Merge pull request #6057 from weseek/support/apply-next18next

support: Relocate locale files and remove locale-utils
Yuki Takei 3 лет назад
Родитель
Сommit
3482d54f1a

+ 0 - 0
packages/app/resource/locales/en_US/admin/admin.json → packages/app/public/static/locales/en_US/admin/admin.json


+ 0 - 0
packages/app/resource/locales/en_US/meta.json → packages/app/public/static/locales/en_US/meta.json


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


+ 0 - 0
packages/app/resource/locales/index.js → packages/app/public/static/locales/index.js


+ 0 - 0
packages/app/resource/locales/ja_JP/admin/admin.json → packages/app/public/static/locales/ja_JP/admin/admin.json


+ 0 - 0
packages/app/resource/locales/ja_JP/meta.json → packages/app/public/static/locales/ja_JP/meta.json


+ 0 - 0
packages/app/resource/locales/ja_JP/translation.json → packages/app/public/static/locales/ja_JP/translation.json


+ 0 - 0
packages/app/resource/locales/zh_CN/admin/admin.json → packages/app/public/static/locales/zh_CN/admin/admin.json


+ 0 - 0
packages/app/resource/locales/zh_CN/meta.json → packages/app/public/static/locales/zh_CN/meta.json


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


+ 36 - 40
packages/app/src/client/admin.jsx

@@ -1,55 +1,52 @@
 import React from 'react';
+
 import ReactDOM from 'react-dom';
-import { Provider } from 'unstated';
 import { I18nextProvider } from 'react-i18next';
-
 import { SWRConfig } from 'swr';
+import { Provider } from 'unstated';
 
-import loggerFactory from '~/utils/logger';
-import { swrGlobalConfiguration } from '~/utils/swr-utils';
-
-import ErrorBoundary from '../components/ErrorBoudary';
-
-import AdminHome from '../components/Admin/AdminHome/AdminHome';
-import UserGroupDetailPage from '../components/Admin/UserGroupDetail/UserGroupDetailPage';
-import NotificationSetting from '../components/Admin/Notification/NotificationSetting';
-import LegacySlackIntegration from '../components/Admin/LegacySlackIntegration/LegacySlackIntegration';
-import SlackIntegration from '../components/Admin/SlackIntegration/SlackIntegration';
-import ManageGlobalNotification from '../components/Admin/Notification/ManageGlobalNotification';
-import MarkdownSetting from '../components/Admin/MarkdownSetting/MarkDownSetting';
-import UserManagement from '../components/Admin/UserManagement';
-import AppSettingsPage from '../components/Admin/App/AppSettingsPage';
-import SecurityManagement from '../components/Admin/Security/SecurityManagement';
-import ManageExternalAccount from '../components/Admin/ManageExternalAccount';
-import UserGroupPage from '../components/Admin/UserGroup/UserGroupPage';
-import Customize from '../components/Admin/Customize/Customize';
-import ImportDataPage from '../components/Admin/ImportDataPage';
-import ExportArchiveDataPage from '../components/Admin/ExportArchiveDataPage';
-import FullTextSearchManagement from '../components/Admin/FullTextSearchManagement';
-import AdminNavigation from '../components/Admin/Common/AdminNavigation';
-
-import AdminSocketIoContainer from '~/client/services/AdminSocketIoContainer';
-import AdminHomeContainer from '~/client/services/AdminHomeContainer';
-import AdminCustomizeContainer from '~/client/services/AdminCustomizeContainer';
-import AdminUserGroupDetailContainer from '~/client/services/AdminUserGroupDetailContainer';
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import AdminAppContainer from '~/client/services/AdminAppContainer';
-import AdminImportContainer from '~/client/services/AdminImportContainer';
-import AdminMarkDownContainer from '~/client/services/AdminMarkDownContainer';
+import AdminBasicSecurityContainer from '~/client/services/AdminBasicSecurityContainer';
+import AdminCustomizeContainer from '~/client/services/AdminCustomizeContainer';
 import AdminExternalAccountsContainer from '~/client/services/AdminExternalAccountsContainer';
 import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
+import AdminGitHubSecurityContainer from '~/client/services/AdminGitHubSecurityContainer';
+import AdminGoogleSecurityContainer from '~/client/services/AdminGoogleSecurityContainer';
+import AdminHomeContainer from '~/client/services/AdminHomeContainer';
+import AdminImportContainer from '~/client/services/AdminImportContainer';
 import AdminLdapSecurityContainer from '~/client/services/AdminLdapSecurityContainer';
 import AdminLocalSecurityContainer from '~/client/services/AdminLocalSecurityContainer';
-import AdminSamlSecurityContainer from '~/client/services/AdminSamlSecurityContainer';
-import AdminOidcSecurityContainer from '~/client/services/AdminOidcSecurityContainer';
-import AdminBasicSecurityContainer from '~/client/services/AdminBasicSecurityContainer';
-import AdminGoogleSecurityContainer from '~/client/services/AdminGoogleSecurityContainer';
-import AdminGitHubSecurityContainer from '~/client/services/AdminGitHubSecurityContainer';
-import AdminTwitterSecurityContainer from '~/client/services/AdminTwitterSecurityContainer';
+import AdminMarkDownContainer from '~/client/services/AdminMarkDownContainer';
 import AdminNotificationContainer from '~/client/services/AdminNotificationContainer';
+import AdminOidcSecurityContainer from '~/client/services/AdminOidcSecurityContainer';
+import AdminSamlSecurityContainer from '~/client/services/AdminSamlSecurityContainer';
 import AdminSlackIntegrationLegacyContainer from '~/client/services/AdminSlackIntegrationLegacyContainer';
-
+import AdminSocketIoContainer from '~/client/services/AdminSocketIoContainer';
+import AdminTwitterSecurityContainer from '~/client/services/AdminTwitterSecurityContainer';
+import AdminUserGroupDetailContainer from '~/client/services/AdminUserGroupDetailContainer';
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import ContextExtractor from '~/client/services/ContextExtractor';
+import loggerFactory from '~/utils/logger';
+import { swrGlobalConfiguration } from '~/utils/swr-utils';
+
+import AdminHome from '../components/Admin/AdminHome/AdminHome';
+import AppSettingsPage from '../components/Admin/App/AppSettingsPage';
+import AdminNavigation from '../components/Admin/Common/AdminNavigation';
+import Customize from '../components/Admin/Customize/Customize';
+import ExportArchiveDataPage from '../components/Admin/ExportArchiveDataPage';
+import FullTextSearchManagement from '../components/Admin/FullTextSearchManagement';
+import ImportDataPage from '../components/Admin/ImportDataPage';
+import LegacySlackIntegration from '../components/Admin/LegacySlackIntegration/LegacySlackIntegration';
+import ManageExternalAccount from '../components/Admin/ManageExternalAccount';
+import MarkdownSetting from '../components/Admin/MarkdownSetting/MarkDownSetting';
+import ManageGlobalNotification from '../components/Admin/Notification/ManageGlobalNotification';
+import NotificationSetting from '../components/Admin/Notification/NotificationSetting';
+import SecurityManagement from '../components/Admin/Security/SecurityManagement';
+import SlackIntegration from '../components/Admin/SlackIntegration/SlackIntegration';
+import UserGroupPage from '../components/Admin/UserGroup/UserGroupPage';
+import UserGroupDetailPage from '../components/Admin/UserGroupDetail/UserGroupDetailPage';
+import UserManagement from '../components/Admin/UserManagement';
+import ErrorBoundary from '../components/ErrorBoudary';
 
 import { appContainer, componentMappings } from './base';
 
@@ -58,7 +55,6 @@ const logger = loggerFactory('growi:admin');
 appContainer.initContents();
 
 const { i18n } = appContainer;
-
 // create unstated container instance
 const adminAppContainer = new AdminAppContainer(appContainer);
 const adminImportContainer = new AdminImportContainer(appContainer);

+ 7 - 1
packages/app/src/client/services/AppContainer.js

@@ -15,7 +15,13 @@ export default class AppContainer extends Container {
 
     this.config = JSON.parse(document.getElementById('growi-context-hydrate').textContent || '{}');
 
-    const userLocaleId = this.currentUser?.lang;
+    // init i18n
+    const currentUserElem = document.getElementById('growi-current-user');
+    let userLocaleId;
+    if (currentUserElem != null) {
+      const currentUser = JSON.parse(currentUserElem.textContent);
+      userLocaleId = currentUser?.lang;
+    }
     this.i18n = i18nFactory(userLocaleId);
 
     this.containerInstances = {};

+ 3 - 2
packages/app/src/client/util/i18n.js

@@ -1,7 +1,8 @@
 import i18n from 'i18next';
 import LanguageDetector from 'i18next-browser-languagedetector';
 import { initReactI18next } from 'react-i18next';
-import locales from '^/resource/locales';
+
+import locales from '^/public/static/locales';
 
 const aliasesMapping = {};
 Object.values(locales).forEach((locale) => {
@@ -13,7 +14,7 @@ Object.values(locales).forEach((locale) => {
   });
 });
 
-// extract metadata list from 'resource/locales/${locale}/meta.json'
+// extract metadata list from 'public/static/locales/${locale}/meta.json'
 export const localeMetadatas = Object.values(locales).map(locale => locale.meta);
 
 export const i18nFactory = (userLocaleId) => {

+ 10 - 0
packages/app/src/next-i18next.config.ts

@@ -0,0 +1,10 @@
+import path from 'path';
+
+export const
+  i18n = {
+    defaultLocale: 'en_US',
+    locales: ['ja_JP', 'zh_CN'],
+  };
+export const defaultNS = 'translation';
+export const localePath = path.resolve('./public/static/locales');
+export const allLocales = [i18n.defaultLocale].concat(i18n.locales);

+ 5 - 4
packages/app/src/server/crowi/dev.js

@@ -1,9 +1,10 @@
 import path from 'path';
-import { listLocaleIds } from '~/utils/locale-utils';
+
+import { allLocales } from '~/next-i18next.config';
 import loggerFactory from '~/utils/logger';
 
-const swig = require('swig-templates');
 const onHeaders = require('on-headers');
+const swig = require('swig-templates');
 
 const logger = loggerFactory('growi:crowi:dev');
 
@@ -41,9 +42,9 @@ class CrowiDev {
    */
   requireForAutoReloadServer() {
     // load all json files for live reloading
-    listLocaleIds()
+    allLocales
       .forEach((localeId) => {
-        require(path.join(this.crowi.localeDir, localeId, 'translation.json'));
+        require(path.join(this.crowi.publicDir, 'static/locales', localeId, 'translation.json'));
       });
   }
 

+ 5 - 4
packages/app/src/server/crowi/express-init.js

@@ -1,5 +1,7 @@
 import mongoose from 'mongoose';
 
+import { allLocales, localePath } from '~/next-i18next.config';
+
 module.exports = function(crowi, app) {
   const debug = require('debug')('growi:crowi:express-init');
   const path = require('path');
@@ -24,7 +26,6 @@ module.exports = function(crowi, app) {
   const registerSafeRedirect = require('../middlewares/safe-redirect')();
   const injectCurrentuserToLocalvars = require('../middlewares/inject-currentuser-to-localvars')();
   const autoReconnectToS2sMsgServer = require('../middlewares/auto-reconnect-to-s2s-msg-server')(crowi);
-  const { listLocaleIds } = require('~/utils/locale-utils');
 
   const avoidSessionRoutes = require('../routes/avoid-session-routes');
   const i18nUserSettingDetector = require('../util/i18nUserSettingDetector');
@@ -41,9 +42,9 @@ module.exports = function(crowi, app) {
     .init({
       // debug: true,
       fallbackLng: ['en_US'],
-      whitelist: listLocaleIds(),
+      whitelist: allLocales,
       backend: {
-        loadPath: `${crowi.localeDir}{{lng}}/translation.json`,
+        loadPath: `${localePath}/{{lng}}/translation.json`,
       },
       detection: {
         order: ['userSettingDetector', 'header', 'navigator'],
@@ -81,7 +82,7 @@ module.exports = function(crowi, app) {
     res.locals.consts = {
       pageGrants: Page.getGrantLabels(),
       userStatus: User.getUserStatusLabels(),
-      language:   listLocaleIds(),
+      language:   allLocales,
       restrictGuestMode: crowi.aclService.getRestrictGuestModeLabels(),
       registrationMode: crowi.aclService.getRegistrationModeLabels(),
     };

+ 2 - 8
packages/app/src/server/models/user.js

@@ -1,4 +1,5 @@
 /* eslint-disable no-use-before-define */
+import { allLocales } from '~/next-i18next.config';
 import { generateGravatarSrc } from '~/utils/gravatar';
 import loggerFactory from '~/utils/logger';
 
@@ -6,15 +7,12 @@ import loggerFactory from '~/utils/logger';
 const crypto = require('crypto');
 
 const debug = require('debug')('growi:models:user');
-const md5 = require('md5');
 const mongoose = require('mongoose');
 const mongoosePaginate = require('mongoose-paginate-v2');
 const uniqueValidator = require('mongoose-unique-validator');
 
 const ObjectId = mongoose.Schema.Types.ObjectId;
 
-const { listLocaleIds, migrateDeprecatedLocaleId } = require('~/utils/locale-utils');
-
 const { omitInsecureAttributes } = require('./serializers/user-serializer');
 
 const logger = loggerFactory('growi:models:user');
@@ -61,7 +59,7 @@ module.exports = function(crowi) {
     apiToken: { type: String, index: true },
     lang: {
       type: String,
-      enum: listLocaleIds(),
+      enum: allLocales,
       default: 'en_US',
     },
     status: {
@@ -78,10 +76,6 @@ module.exports = function(crowi) {
       },
     },
   });
-  // eslint-disable-next-line prefer-arrow-callback
-  userSchema.pre('validate', function() {
-    this.lang = migrateDeprecatedLocaleId(this.lang);
-  });
   userSchema.plugin(mongoosePaginate);
   userSchema.plugin(uniqueValidator);
 

+ 3 - 4
packages/app/src/server/routes/apiv3/app-settings.js

@@ -1,4 +1,6 @@
 import { body } from 'express-validator';
+
+import { allLocales } from '~/next-i18next.config';
 import loggerFactory from '~/utils/logger';
 
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
@@ -6,11 +8,8 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 const logger = loggerFactory('growi:routes:apiv3:app-settings');
 
 const debug = require('debug')('growi:routes:admin');
-
 const express = require('express');
-
 const { pathUtils } = require('@growi/core');
-const { listLocaleIds } = require('~/utils/locale-utils');
 
 const router = express.Router();
 
@@ -157,7 +156,7 @@ module.exports = (crowi) => {
     appSetting: [
       body('title').trim(),
       body('confidential'),
-      body('globalLang').isIn(listLocaleIds()),
+      body('globalLang').isIn(allLocales),
       body('isEmailPublishedForNewUser').isBoolean(),
       body('fileUpload').isBoolean(),
     ],

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

@@ -1,6 +1,6 @@
 import { body } from 'express-validator';
 
-import { listLocaleIds } from '~/utils/locale-utils';
+import { allLocales } from '~/next-i18next.config';
 import loggerFactory from '~/utils/logger';
 
 
@@ -83,7 +83,7 @@ module.exports = (crowi) => {
           if (!User.isEmailValid(email)) throw new Error('email is not included in whitelist');
           return true;
         }),
-      body('lang').isString().isIn(listLocaleIds()),
+      body('lang').isString().isIn(allLocales),
       body('isEmailPublished').isBoolean(),
       body('slackMemberId').optional().isString(),
     ],

+ 0 - 50
packages/app/src/utils/locale-utils.ts

@@ -1,50 +0,0 @@
-import fs from 'fs';
-
-import { resolveFromRoot } from '~/utils/project-dir-utils';
-
-const MIGRATE_LOCALE_MAP = {
-  en: 'en_US',
-  ja: 'ja_JP',
-};
-
-/**
- * List locales dirents
- */
-function listLocaleDirents() {
-  const allDirents = fs.readdirSync(resolveFromRoot('./resource/locales'), { withFileTypes: true });
-  return allDirents
-    .filter(dirent => dirent.isDirectory());
-}
-
-/**
- * List locales aliases
- */
-function listLocaleMetadatas() {
-  return listLocaleDirents()
-    .map(dir => dir.name)
-    .map(localeDirName => require(`../../resource/locales/${localeDirName}/meta.json`));
-}
-
-/**
- * List locales IDs (=subdir names)
- */
-function listLocaleIds() {
-  return listLocaleMetadatas()
-    .map(meta => meta.id);
-}
-
-function migrateDeprecatedLocaleId(localeId) {
-  const toValue = MIGRATE_LOCALE_MAP[localeId];
-
-  if (toValue != null) {
-    return toValue;
-  }
-
-  return localeId;
-}
-
-module.exports = {
-  listLocaleMetadatas,
-  listLocaleIds,
-  migrateDeprecatedLocaleId,
-};