Jelajahi Sumber

fix: support extended language tags (#2224)

Gerald 11 bulan lalu
induk
melakukan
33918fbfe5
2 mengubah file dengan 43 tambahan dan 7 penghapusan
  1. 28 6
      src/common/index.js
  2. 15 1
      test/common/index.test.js

+ 28 - 6
src/common/index.js

@@ -150,15 +150,37 @@ export function leftpad(input, length, pad = '0') {
   return num;
 }
 
+/**
+ * @param {string} browserLang  Language tags from RFC5646 (`[lang]-[script]-[region]-[variant]`, all parts are optional)
+ * @param {string} locale  `<lang>`, `<lang>-<region>`
+ */
+function localeMatch(browserLang, metaLocale) {
+  const bParts = browserLang.toLowerCase().split('-');
+  const mParts = metaLocale.toLowerCase().split('-');
+  let bi = 0;
+  let mi = 0;
+  while (bi < bParts.length && mi < mParts.length) {
+    if (bParts[bi] === mParts[mi]) mi += 1;
+    bi += 1;
+  }
+  return mi === mParts.length;
+}
+
 /**
  * Get locale attributes such as `@name:zh-CN`
  */
-export function getLocaleString(meta, key) {
-  const localeMeta = navigator.languages
-  // Use `lang.toLowerCase()` since v2.6.5
-  .map(lang => meta[`${key}:${lang}`] || meta[`${key}:${lang.toLowerCase()}`])
-  .find(Boolean);
-  return localeMeta || meta[key] || '';
+export function getLocaleString(meta, key, languages = navigator.languages) {
+  // zh, zh-cn, zh-tw
+  const mls = Object.keys(meta)
+    .filter(metaKey => metaKey.startsWith(key + ':'))
+    .map(metaKey => metaKey.slice(key.length + 1))
+    .sort((a, b) => b.length - a.length);
+  let bestLocale;
+  for (const lang of languages) {
+    bestLocale = mls.find(ml => localeMatch(lang, ml));
+    if (bestLocale) break;
+  }
+  return meta[bestLocale ? `${key}:${bestLocale}` : key] || '';
 }
 
 /**

+ 15 - 1
test/common/index.test.js

@@ -1,6 +1,6 @@
 import {
   isRemote, compareVersion, debounce, throttle,
-  loadQuery, dumpQuery,
+  loadQuery, dumpQuery, getLocaleString,
 } from '@/common';
 
 jest.useFakeTimers();
@@ -155,3 +155,17 @@ test('loadQuery/dumpQuery', () => {
   expect(obj).toEqual({ a: '|#,', b: '', c: '' });
   expect(dumpQuery(obj)).toEqual(normalized);
 });
+
+test('getLocaleString', () => {
+  const meta = {
+    'name': 'name without locale',
+    'name:zh': 'name for zh',
+    'name:zh-CN': 'name for zh-CN',
+    'name:zh-TW': 'name for zh-TW',
+  };
+  expect(getLocaleString(meta, 'name', ['zh-CN', 'zh'])).toBe('name for zh-CN');
+  expect(getLocaleString(meta, 'name', ['zh', 'zh-CN'])).toBe('name for zh');
+  expect(getLocaleString(meta, 'name', ['zh-Hant-TW', 'zh-Hant', 'zh'])).toBe('name for zh-TW');
+  expect(getLocaleString(meta, 'name', ['zh-Hant', 'zh'])).toBe('name for zh');
+  expect(getLocaleString(meta, 'name', ['en', 'en-US'])).toBe('name without locale');
+});