locale-utils.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import { Lang } from '@growi/core/dist/interfaces';
  2. import type { IncomingHttpHeaders } from 'http';
  3. import * as i18nextConfig from '^/config/i18next.config';
  4. // Re-export getLocale from the shared client-safe module
  5. export { getLocale } from '~/utils/locale-utils';
  6. const ACCEPT_LANG_MAP = {
  7. en: Lang.en_US,
  8. ja: Lang.ja_JP,
  9. zh: Lang.zh_CN,
  10. fr: Lang.fr_FR,
  11. ko: Lang.ko_KR,
  12. };
  13. /**
  14. * It return the first language that matches ACCEPT_LANG_MAP keys from sorted accept languages array
  15. * @param sortedAcceptLanguagesArray
  16. */
  17. const getPreferredLanguage = (sortedAcceptLanguagesArray: string[]): Lang => {
  18. for (const lang of sortedAcceptLanguagesArray) {
  19. const matchingLang = Object.keys(ACCEPT_LANG_MAP).find((key) =>
  20. lang.includes(key),
  21. );
  22. if (matchingLang) return ACCEPT_LANG_MAP[matchingLang];
  23. }
  24. return i18nextConfig.defaultLang;
  25. };
  26. /**
  27. * Detect locale from browser accept language
  28. * @param headers
  29. */
  30. export const detectLocaleFromBrowserAcceptLanguage = (
  31. headers: IncomingHttpHeaders,
  32. ): Lang => {
  33. // 1. get the header accept-language
  34. // ex. "ja,ar-SA;q=0.8,en;q=0.6,en-CA;q=0.4,en-US;q=0.2"
  35. const acceptLanguages = headers['accept-language'];
  36. if (acceptLanguages == null) {
  37. return i18nextConfig.defaultLang;
  38. }
  39. // 1. trim blank spaces.
  40. // 2. separate by ,.
  41. // 3. if "lang;q=x", then { 'x', 'lang' } to add to the associative array.
  42. // if "lang" has no weight x (";q=x"), add it with key = 1.
  43. // ex. {'1': 'ja','0.8': 'ar-SA','0.6': 'en','0.4': 'en-CA','0.2': 'en-US'}
  44. const acceptLanguagesDict = acceptLanguages
  45. .replace(/\s+/g, '')
  46. .split(',')
  47. .map((item) => item.split(/\s*;\s*q\s*=\s*/))
  48. .reduce((acc, [key, value = '1']) => {
  49. acc[value] = key;
  50. return acc;
  51. }, {});
  52. // 1. create an array of sorted languages in descending order.
  53. // ex. [ 'ja', 'ar-SA', 'en', 'en-CA', 'en-US' ]
  54. const sortedAcceptLanguagesArray = Object.keys(acceptLanguagesDict)
  55. .sort((x, y) => y.localeCompare(x))
  56. .map((item) => acceptLanguagesDict[item]);
  57. return getPreferredLanguage(sortedAcceptLanguagesArray);
  58. };