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

refactor detect locale from browser accept language

jam411 3 лет назад
Родитель
Сommit
e6da06c549
1 измененных файлов с 53 добавлено и 44 удалено
  1. 53 44
      packages/app/src/pages/utils/commons.ts

+ 53 - 44
packages/app/src/pages/utils/commons.ts

@@ -1,6 +1,8 @@
+import type { IncomingHttpHeaders } from 'http';
+
 import type { ColorScheme, IUserHasId } from '@growi/core';
 import type { ColorScheme, IUserHasId } from '@growi/core';
 import {
 import {
-  DevidedPagePath, Lang, AllLang,
+  DevidedPagePath, Lang, AllLang, acceptLangMap,
 } from '@growi/core';
 } from '@growi/core';
 import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
 import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
 import type { SSRConfig, UserConfig } from 'next-i18next';
 import type { SSRConfig, UserConfig } from 'next-i18next';
@@ -89,6 +91,55 @@ export const getServerSideCommonProps: GetServerSideProps<CommonProps> = async(c
   return { props };
   return { props };
 };
 };
 
 
+/**
+ * It return the first language that matches acceptLangMap keys from sorted accept languages array
+ * @param sortedAcceptLanguagesArray
+ */
+const getPreferredLanguage = (sortedAcceptLanguagesArray: string[]): Lang => {
+  for (const lang of sortedAcceptLanguagesArray) {
+    const matchingLang = Object.keys(acceptLangMap).find(key => lang.includes(key));
+    if (matchingLang) return acceptLangMap[matchingLang];
+  }
+  return nextI18NextConfig.defalutLang;
+};
+
+/**
+  * Detect locale from browser accept language
+  * @param headers
+  */
+const detectLocaleFromBrowserAcceptLanguage = (headers: IncomingHttpHeaders) => {
+  // 1. get the header accept-language
+  // ex. "ja,ar-SA;q=0.8,en;q=0.6,en-CA;q=0.4,en-US;q=0.2"
+  const acceptLanguages = headers['accept-language'];
+
+  if (acceptLanguages == null) {
+    return nextI18NextConfig.defalutLang;
+  }
+
+  // 1. trim blank spaces.
+  // 2. separate by ,.
+  // 3. if "lang;q=x", then { 'x', 'lang' } to add to the associative array.
+  //    if "lang" has no weight x (";q=x"), add it with key = 1.
+  // ex. {'1': 'ja','0.8': 'ar-SA','0.6': 'en','0.4': 'en-CA','0.2': 'en-US'}
+  const acceptLanguagesDict = acceptLanguages
+    .replace(/\s+/g, '')
+    .split(',')
+    .map(item => item.split(/\s*;\s*q\s*=\s*/))
+    .reduce((acc, [key, value = '1']) => {
+      acc[value] = key;
+      return acc;
+    }, {});
+
+  // 1. create an array of sorted languages in descending order.
+  // ex. [ 'ja', 'ar-SA', 'en', 'en-CA', 'en-US' ]
+  const sortedAcceptLanguagesArray = Object.keys(acceptLanguagesDict)
+    .sort((x, y) => y.localeCompare(x))
+    .map(item => acceptLanguagesDict[item]);
+
+  return getPreferredLanguage(sortedAcceptLanguagesArray);
+};
+
+
 export const getNextI18NextConfig = async(
 export const getNextI18NextConfig = async(
     // 'serverSideTranslations' method should be given from Next.js Page
     // 'serverSideTranslations' method should be given from Next.js Page
     //  because importing it in this file causes https://github.com/isaachinman/next-i18next/issues/1545
     //  because importing it in this file causes https://github.com/isaachinman/next-i18next/issues/1545
@@ -102,50 +153,8 @@ export const getNextI18NextConfig = async(
   const { crowi, user, headers } = req;
   const { crowi, user, headers } = req;
   const { configManager } = crowi;
   const { configManager } = crowi;
 
 
-  // detect locale from browser accept language
-  const detectLocaleFromBrowserAcceptLanguage = () => {
-    // get the header accept-language
-    // ex. "ja,ar-SA;q=0.8,en;q=0.6,en-CA;q=0.4,en-US;q=0.2"
-    const acceptLanguages = headers['accept-language'];
-
-    if (acceptLanguages == null) {
-      return Lang.en_US;
-    }
-
-    // 1. trim blank spaces.
-    // 2. separate by ,.
-    // 3. if "lang;q=x", then { 'x', 'lang' } to add to the associative array.
-    //    if "lang" has no weight x (";q=x"), add it with key = 1.
-    // ex. {'1': 'ja','0.8': 'ar-SA','0.6': 'en','0.4': 'en-CA','0.2': 'en-US'}
-    const acceptLanguagesDict = acceptLanguages
-      .replace(/\s+/g, '')
-      .split(',')
-      .map(item => item.split(/\s*;\s*q\s*=\s*/))
-      .reduce((acc, [key, value = '1']) => {
-        acc[value] = key;
-        return acc;
-      }, {});
-
-    // create an array of sorted languages in descending order.
-    // ex. [ 'ja', 'ar-SA', 'en', 'en-CA', 'en-US' ]
-    const sortedAcceptLanguagesArray = Object.keys(acceptLanguagesDict)
-      .sort((a, b) => b.localeCompare(a))
-      .map(item => acceptLanguagesDict[item]);
-
-    // it return the first language that matches 'en', 'ja' or 'zh.
-    // if no matching language is found, it returns Lang.en_US as a default.
-    for (const lang of sortedAcceptLanguagesArray) {
-      if (lang.includes('en')) return Lang.en_US;
-      if (lang.includes('ja')) return Lang.ja_JP;
-      if (lang.includes('zh')) return Lang.zh_CN;
-    }
-    return Lang.en_US;
-  };
-
-  const detectLocale = detectLocaleFromBrowserAcceptLanguage();
-
   // determine language
   // determine language
-  const locale = user == null ? detectLocale
+  const locale = user == null ? detectLocaleFromBrowserAcceptLanguage(headers)
     : (user.lang ?? configManager.getConfig('crowi', 'app:globalLang') as Lang ?? Lang.en_US);
     : (user.lang ?? configManager.getConfig('crowi', 'app:globalLang') as Lang ?? Lang.en_US);
 
 
   const namespaces = ['commons'];
   const namespaces = ['commons'];