From 3902db5c0b3aa1706cfa2f717f3507832b0949b8 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Tue, 20 Aug 2024 20:00:33 +0800 Subject: [PATCH] Handle kab issue temporarily --- scripts/catalogs.js | 60 ++++++++--- src/components/lang-selector.jsx | 8 +- src/data/catalogs.json | 164 ++++++++++++++++++++----------- src/utils/lang.js | 12 ++- 4 files changed, 165 insertions(+), 79 deletions(-) diff --git a/scripts/catalogs.js b/scripts/catalogs.js index e93fcba5..39e19450 100644 --- a/scripts/catalogs.js +++ b/scripts/catalogs.js @@ -15,6 +15,10 @@ const enPo = PO.parse(enContent); const total = enPo.items.length; console.log('Total strings:', total); +const codeMaps = { + 'kab-KAB': 'kab', +}; + files.forEach((file) => { if (file.endsWith('.po')) { const code = file.replace(/\.po$/, ''); @@ -30,31 +34,57 @@ files.forEach((file) => { po.percentage = percentage; if (percentage > 0) { // Ignore empty catalogs - catalogs[code] = percentage; + catalogs[codeMaps[code] || code] = percentage; } } }); +const regionMaps = { + 'zh-CN': 'zh-Hans', + 'zh-TW': 'zh-Hant', +}; + +function IDN(inputCode, outputCode) { + let result; + const regionlessInputCode = + regionMaps[inputCode] || inputCode.replace(/-[a-z]+$/i, ''); + const regionlessOutputCode = + regionMaps[outputCode] || outputCode.replace(/-[a-z]+$/i, ''); + const inputCodes = + regionlessInputCode !== inputCode + ? [inputCode, regionlessInputCode] + : [inputCode]; + const outputCodes = + regionlessOutputCode !== outputCode + ? [regionlessOutputCode, outputCode] + : [outputCode]; + + for (const inputCode of inputCodes) { + for (const outputCode of outputCodes) { + try { + result = new Intl.DisplayNames([inputCode], { + type: 'language', + }).of(outputCode); + break; + } catch (e) {} + } + if (result) break; + } + return result; +} + // Sort by percentage const sortedCatalogs = Object.entries(catalogs) .sort((a, b) => b[1] - a[1]) - .map(([code, percentage]) => { - const name = new Intl.DisplayNames(['en'], { type: 'language' }).of(code); - return { code, name, percentage }; + .map(([code, completion]) => { + const nativeName = IDN(code, code); + const name = IDN('en', code); + // let names = {}; + return { code, nativeName, name, completion }; }); console.table(sortedCatalogs); const path = 'src/data/catalogs.json'; -fs.writeFileSync( - path, - JSON.stringify( - Object.entries(catalogs).map(([code, percentage]) => ({ - code, - completion: percentage, - })), - null, - 2, - ), -); +fs.writeFileSync(path, JSON.stringify(sortedCatalogs, null, 2)); console.log('File written:', path); diff --git a/src/components/lang-selector.jsx b/src/components/lang-selector.jsx index 6592a3ea..cdf261c7 100644 --- a/src/components/lang-selector.jsx +++ b/src/components/lang-selector.jsx @@ -1,7 +1,7 @@ import { useLingui } from '@lingui/react'; import { useMemo } from 'preact/hooks'; -import { DEFAULT_LANG, LOCALES } from '../locales'; +import { CATALOGS, DEFAULT_LANG, LOCALES } from '../locales'; import { activateLang } from '../utils/lang'; import localeCode2Text from '../utils/localeCode2Text'; @@ -24,7 +24,11 @@ export default function LangSelector() { // Wait till there's too many languages and there are regional clashes const regionlessCode = regionMaps[lang] || lang.replace(/-[a-z]+$/i, ''); - const native = localeCode2Text({ code: regionlessCode, locale: lang }); + const native = localeCode2Text({ + code: regionlessCode, + locale: lang, + fallback: CATALOGS[lang]?.nativeName || lang, + }); // Not used when rendering because it'll change based on current locale // Only used for sorting on render diff --git a/src/data/catalogs.json b/src/data/catalogs.json index 7b3989e5..b4a1f14c 100644 --- a/src/data/catalogs.json +++ b/src/data/catalogs.json @@ -1,90 +1,134 @@ [ - { - "code": "ar-SA", - "completion": 22 - }, { "code": "ca-ES", + "nativeName": "català", + "name": "Catalan", "completion": 100 }, - { - "code": "cs-CZ", - "completion": 72 - }, - { - "code": "de-DE", - "completion": 99 - }, - { - "code": "eo-UY", - "completion": 15 - }, { "code": "es-ES", + "nativeName": "español", + "name": "Spanish", "completion": 100 }, { "code": "eu-ES", + "nativeName": "euskara", + "name": "Basque", "completion": 100 }, - { - "code": "fa-IR", - "completion": 62 - }, { "code": "fi-FI", + "nativeName": "suomi", + "name": "Finnish", "completion": 100 }, - { - "code": "fr-FR", - "completion": 95 - }, { "code": "gl-ES", + "nativeName": "galego", + "name": "Galician", "completion": 100 }, - { - "code": "he-IL", - "completion": 11 - }, - { - "code": "it-IT", - "completion": 13 - }, - { - "code": "ja-JP", - "completion": 32 - }, - { - "code": "kab-KAB", - "completion": 54 - }, - { - "code": "ko-KR", - "completion": 76 - }, - { - "code": "lt-LT", - "completion": 25 - }, - { - "code": "nl-NL", - "completion": 49 - }, - { - "code": "ru-RU", - "completion": 24 - }, - { - "code": "th-TH", - "completion": 3 - }, { "code": "zh-CN", + "nativeName": "简体中文", + "name": "Simplified Chinese", "completion": 100 }, + { + "code": "de-DE", + "nativeName": "Deutsch", + "name": "German", + "completion": 99 + }, + { + "code": "fr-FR", + "nativeName": "français", + "name": "French", + "completion": 95 + }, + { + "code": "ko-KR", + "nativeName": "한국어", + "name": "Korean", + "completion": 76 + }, + { + "code": "cs-CZ", + "nativeName": "čeština", + "name": "Czech", + "completion": 72 + }, + { + "code": "fa-IR", + "nativeName": "فارسی", + "name": "Persian", + "completion": 62 + }, + { + "code": "kab", + "nativeName": "Taqbaylit", + "name": "Kabyle", + "completion": 54 + }, + { + "code": "nl-NL", + "nativeName": "Nederlands", + "name": "Dutch", + "completion": 49 + }, + { + "code": "ja-JP", + "nativeName": "日本語", + "name": "Japanese", + "completion": 32 + }, + { + "code": "lt-LT", + "nativeName": "lietuvių", + "name": "Lithuanian", + "completion": 25 + }, + { + "code": "ru-RU", + "nativeName": "русский", + "name": "Russian", + "completion": 24 + }, + { + "code": "ar-SA", + "nativeName": "العربية", + "name": "Arabic", + "completion": 22 + }, + { + "code": "eo-UY", + "nativeName": "Esperanto", + "name": "Esperanto", + "completion": 15 + }, + { + "code": "it-IT", + "nativeName": "italiano", + "name": "Italian", + "completion": 13 + }, + { + "code": "he-IL", + "nativeName": "עברית", + "name": "Hebrew", + "completion": 11 + }, + { + "code": "th-TH", + "nativeName": "ไทย", + "name": "Thai", + "completion": 3 + }, { "code": "zh-TW", + "nativeName": "繁體中文", + "name": "Traditional Chinese", "completion": 3 } ] \ No newline at end of file diff --git a/src/utils/lang.js b/src/utils/lang.js index b17e252d..16cd5ca3 100644 --- a/src/utils/lang.js +++ b/src/utils/lang.js @@ -13,6 +13,10 @@ import localeMatch from '../utils/locale-match'; const { PHANPY_DEFAULT_LANG } = import.meta.env; +const langFileMaps = { + kab: 'kab-KAB', +}; + i18n.load(DEFAULT_LANG, messages); i18n.on('change', () => { const lang = i18n.locale; @@ -35,10 +39,13 @@ export async function activateLang(lang) { console.log('💬 ACTIVATE LANG', DEFAULT_LANG, lang); } else { try { - const { messages } = await import(`../locales/${lang}.po`); + const { messages } = await import( + `../locales/${langFileMaps[lang] || lang}.po` + ); i18n.loadAndActivate({ locale: lang, messages }); console.log('💬 ACTIVATE LANG', lang, messages); } catch (e) { + console.error(e); // Fallback to default language i18n.activate(DEFAULT_LANG); console.log('💬 ACTIVATE LANG', DEFAULT_LANG, lang); @@ -54,7 +61,8 @@ export function initActivateLang() { PHANPY_DEFAULT_LANG, DEFAULT_LANG, ); - const matchedLang = localeMatch(lang, LOCALES); + const matchedLang = + LOCALES.find((l) => l === lang) || localeMatch(lang, LOCALES); activateLang(matchedLang); // const yes = confirm(t`Reload to apply language setting?`);