Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>tags/1.7.2
| 'use client' | |||||
| import { loadLangResources } from '@/i18n-config/i18next-config' | |||||
| import { useCallback, useEffect, useState } from 'react' | |||||
| import cn from '@/utils/classnames' | |||||
| import { LanguagesSupported } from '@/i18n-config/language' | |||||
| export default function I18nTest() { | |||||
| const [langs, setLangs] = useState<Lang[]>([]) | |||||
| const getLangs = useCallback(async () => { | |||||
| const langs = await genLangs() | |||||
| setLangs(langs) | |||||
| }, []) | |||||
| useEffect(() => { | |||||
| getLangs() | |||||
| }, []) | |||||
| return ( | |||||
| <div | |||||
| style={{ | |||||
| height: 'calc(100% - 6em)', | |||||
| overflowY: 'auto', | |||||
| margin: '1em 1em 5em', | |||||
| }} | |||||
| > | |||||
| <div style={{ minHeight: '75vh' }}> | |||||
| <h2>Summary</h2> | |||||
| <table | |||||
| className={cn('mt-2 min-w-[340px] border-collapse border-0')} | |||||
| > | |||||
| <thead className="system-xs-medium-uppercase text-text-tertiary"> | |||||
| <tr> | |||||
| <td className="w-5 min-w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1"> | |||||
| # | |||||
| </td> | |||||
| <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| lang | |||||
| </td> | |||||
| <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| count | |||||
| </td> | |||||
| <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| missing | |||||
| </td> | |||||
| <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| extra | |||||
| </td> | |||||
| </tr> | |||||
| </thead> | |||||
| <tbody className="system-sm-regular text-text-secondary"> | |||||
| {langs.map(({ locale, count, missing, extra }, idx) => <tr key={locale}> | |||||
| <td className="">{idx}</td> | |||||
| <td className="p-1.5">{locale}</td> | |||||
| <td>{count}</td> | |||||
| <td>{missing.length}</td> | |||||
| <td>{extra.length}</td> | |||||
| </tr>)} | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <h2>Details</h2> | |||||
| <table | |||||
| className={cn('mt-2 w-full min-w-[340px] border-collapse border-0')} | |||||
| > | |||||
| <thead className="system-xs-medium-uppercase text-text-tertiary"> | |||||
| <tr> | |||||
| <td className="w-5 min-w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1"> | |||||
| # | |||||
| </td> | |||||
| <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| lang | |||||
| </td> | |||||
| <td className="w-full whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| missing | |||||
| </td> | |||||
| <td className="w-full whitespace-nowrap bg-background-section-burn py-1.5 pl-3"> | |||||
| extra | |||||
| </td> | |||||
| </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| {langs.map(({ locale, missing, extra }, idx) => { | |||||
| return (<tr key={locale}> | |||||
| <td className="py-2 align-top">{idx}</td> | |||||
| <td className="py-2 align-top">{locale}</td> | |||||
| <td className="py-2 align-top"> | |||||
| <ul> | |||||
| {missing.map(key => ( | |||||
| <li key={key}>{key}</li> | |||||
| ))} | |||||
| </ul> | |||||
| </td> | |||||
| <td className="py-2 align-top"> | |||||
| <ul> | |||||
| {extra.map(key => ( | |||||
| <li key={key}>{key}</li> | |||||
| ))} | |||||
| </ul> | |||||
| </td> | |||||
| </tr> | |||||
| ) | |||||
| })} | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| async function genLangs() { | |||||
| const langs_: Lang[] = [] | |||||
| let en!: Lang | |||||
| const resources: Record<string, any> = {} | |||||
| // Initialize empty resource object | |||||
| for (const lang of LanguagesSupported) | |||||
| resources[lang] = await loadLangResources(lang) | |||||
| for (const [key, value] of Object.entries(resources)) { | |||||
| const keys = getNestedKeys(value.translation) | |||||
| const lang: Lang = { | |||||
| locale: key, | |||||
| keys: new Set(keys), | |||||
| count: keys.length, | |||||
| missing: [], | |||||
| extra: [], | |||||
| } | |||||
| langs_.push(lang) | |||||
| if (key === 'en-US') en = lang | |||||
| } | |||||
| for (const lang of langs_) { | |||||
| const missing: string[] = [] | |||||
| const extra: string[] = [] | |||||
| for (const key of lang.keys) | |||||
| if (!en.keys.has(key)) extra.push(key) | |||||
| for (const key of en.keys) | |||||
| if (!lang.keys.has(key)) missing.push(key) | |||||
| lang.missing = missing | |||||
| lang.extra = extra | |||||
| } | |||||
| return langs_ | |||||
| } | |||||
| function getNestedKeys(translation: Record<string, any>): string[] { | |||||
| const nestedKeys: string[] = [] | |||||
| const iterateKeys = (obj: Record<string, any>, prefix = '') => { | |||||
| for (const key in obj) { | |||||
| const nestedKey = prefix ? `${prefix}.${key}` : key | |||||
| // nestedKeys.push(nestedKey); | |||||
| if (typeof obj[key] === 'object') iterateKeys(obj[key], nestedKey) | |||||
| else if (typeof obj[key] === 'string') nestedKeys.push(nestedKey) | |||||
| } | |||||
| } | |||||
| iterateKeys(translation) | |||||
| return nestedKeys | |||||
| } | |||||
| type Lang = { | |||||
| locale: string; | |||||
| keys: Set<string>; | |||||
| count: number; | |||||
| missing: string[]; | |||||
| extra: string[]; | |||||
| } |
| import type React from 'react' | |||||
| import { notFound } from 'next/navigation' | |||||
| export default async function Layout({ children }: React.PropsWithChildren) { | |||||
| if (process.env.NODE_ENV !== 'development') | |||||
| notFound() | |||||
| return children | |||||
| } |
| 'use client' | |||||
| import DemoForm from '../components/base/form/form-scenarios/demo' | |||||
| export default function Page() { | |||||
| return ( | |||||
| <div className='flex h-screen w-full items-center justify-center p-20'> | |||||
| <DemoForm /> | |||||
| </div> | |||||
| ) | |||||
| } |
| const data = require('./languages.json') | const data = require('./languages.json') | ||||
| const targetLanguage = 'en-US' | const targetLanguage = 'en-US' | ||||
| const i18nFolder = '../i18n' // Path to i18n folder relative to this script | |||||
| // https://github.com/plainheart/bing-translate-api/blob/master/src/met/lang.json | // https://github.com/plainheart/bing-translate-api/blob/master/src/met/lang.json | ||||
| const languageKeyMap = data.languages.reduce((map, language) => { | const languageKeyMap = data.languages.reduce((map, language) => { | ||||
| if (language.supported) { | if (language.supported) { | ||||
| } | } | ||||
| })) | })) | ||||
| } | } | ||||
| async function autoGenTrans(fileName, toGenLanguage) { | async function autoGenTrans(fileName, toGenLanguage) { | ||||
| const fullKeyFilePath = path.join(__dirname, targetLanguage, `${fileName}.ts`) | |||||
| const toGenLanguageFilePath = path.join(__dirname, toGenLanguage, `${fileName}.ts`) | |||||
| const fullKeyFilePath = path.join(__dirname, i18nFolder, targetLanguage, `${fileName}.ts`) | |||||
| const toGenLanguageFilePath = path.join(__dirname, i18nFolder, toGenLanguage, `${fileName}.ts`) | |||||
| // eslint-disable-next-line sonarjs/code-eval | // eslint-disable-next-line sonarjs/code-eval | ||||
| const fullKeyContent = eval(transpile(fs.readFileSync(fullKeyFilePath, 'utf8'))) | const fullKeyContent = eval(transpile(fs.readFileSync(fullKeyFilePath, 'utf8'))) | ||||
| // if toGenLanguageFilePath is not exist, create it | // if toGenLanguageFilePath is not exist, create it | ||||
| // Promise.all(Object.keys(languageKeyMap).map(async (toLanguage) => { | // Promise.all(Object.keys(languageKeyMap).map(async (toLanguage) => { | ||||
| // await autoGenTrans(fileName, toLanguage) | // await autoGenTrans(fileName, toLanguage) | ||||
| // })) | // })) | ||||
| const files = fs | const files = fs | ||||
| .readdirSync(path.join(__dirname, targetLanguage)) | |||||
| .readdirSync(path.join(__dirname, i18nFolder, targetLanguage)) | |||||
| .map(file => file.replace(/\.ts/, '')) | .map(file => file.replace(/\.ts/, '')) | ||||
| .filter(f => f !== 'app-debug') // ast parse error in app-debug | .filter(f => f !== 'app-debug') // ast parse error in app-debug | ||||
| async function getKeysFromLanuage(language) { | async function getKeysFromLanuage(language) { | ||||
| return new Promise((resolve, reject) => { | return new Promise((resolve, reject) => { | ||||
| const folderPath = path.join(__dirname, language) | |||||
| const folderPath = path.join(__dirname, '../i18n', language) | |||||
| let allKeys = [] | let allKeys = [] | ||||
| fs.readdir(folderPath, (err, files) => { | fs.readdir(folderPath, (err, files) => { | ||||
| if (err) { | if (err) { |
| "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", | "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", | ||||
| "gen-icons": "node ./app/components/base/icons/script.mjs", | "gen-icons": "node ./app/components/base/icons/script.mjs", | ||||
| "uglify-embed": "node ./bin/uglify-embed", | "uglify-embed": "node ./bin/uglify-embed", | ||||
| "check-i18n": "node ./i18n/check-i18n.js", | |||||
| "auto-gen-i18n": "node ./i18n/auto-gen-i18n.js", | |||||
| "check-i18n": "node ./i18n-config/check-i18n.js", | |||||
| "auto-gen-i18n": "node ./i18n-config/auto-gen-i18n.js", | |||||
| "test": "jest", | "test": "jest", | ||||
| "test:watch": "jest --watch", | "test:watch": "jest --watch", | ||||
| "storybook": "storybook dev -p 6006", | "storybook": "storybook dev -p 6006", | ||||
| "which-typed-array": "npm:@nolyfill/which-typed-array@^1" | "which-typed-array": "npm:@nolyfill/which-typed-array@^1" | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } |