You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

page.tsx 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. 'use client'
  2. import { loadLangResources } from '@/i18n/i18next-config'
  3. import { useCallback, useEffect, useState } from 'react'
  4. import cn from '@/utils/classnames'
  5. import { LanguagesSupported } from '@/i18n/language'
  6. export default function I18nTest() {
  7. const [langs, setLangs] = useState<Lang[]>([])
  8. const getLangs = useCallback(async () => {
  9. const langs = await genLangs()
  10. setLangs(langs)
  11. }, [])
  12. useEffect(() => {
  13. getLangs()
  14. }, [])
  15. return (
  16. <div
  17. style={{
  18. height: 'calc(100% - 6em)',
  19. overflowY: 'auto',
  20. margin: '1em 1em 5em',
  21. }}
  22. >
  23. <div style={{ minHeight: '75vh' }}>
  24. <h2>Summary</h2>
  25. <table
  26. className={cn('mt-2 min-w-[340px] border-collapse border-0')}
  27. >
  28. <thead className="system-xs-medium-uppercase text-text-tertiary">
  29. <tr>
  30. <td className="w-5 min-w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1">
  31. #
  32. </td>
  33. <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  34. lang
  35. </td>
  36. <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  37. count
  38. </td>
  39. <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  40. missing
  41. </td>
  42. <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  43. extra
  44. </td>
  45. </tr>
  46. </thead>
  47. <tbody className="system-sm-regular text-text-secondary">
  48. {langs.map(({ locale, count, missing, extra }, idx) => <tr key={locale}>
  49. <td className="">{idx}</td>
  50. <td className="p-1.5">{locale}</td>
  51. <td>{count}</td>
  52. <td>{missing.length}</td>
  53. <td>{extra.length}</td>
  54. </tr>)}
  55. </tbody>
  56. </table>
  57. </div>
  58. <h2>Details</h2>
  59. <table
  60. className={cn('mt-2 w-full min-w-[340px] border-collapse border-0')}
  61. >
  62. <thead className="system-xs-medium-uppercase text-text-tertiary">
  63. <tr>
  64. <td className="w-5 min-w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1">
  65. #
  66. </td>
  67. <td className="w-20 min-w-20 whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  68. lang
  69. </td>
  70. <td className="w-full whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  71. missing
  72. </td>
  73. <td className="w-full whitespace-nowrap bg-background-section-burn py-1.5 pl-3">
  74. extra
  75. </td>
  76. </tr>
  77. </thead>
  78. <tbody>
  79. {langs.map(({ locale, missing, extra }, idx) => {
  80. return (<tr key={locale}>
  81. <td className="py-2 align-top">{idx}</td>
  82. <td className="py-2 align-top">{locale}</td>
  83. <td className="py-2 align-top">
  84. <ul>
  85. {missing.map(key => (
  86. <li key={key}>{key}</li>
  87. ))}
  88. </ul>
  89. </td>
  90. <td className="py-2 align-top">
  91. <ul>
  92. {extra.map(key => (
  93. <li key={key}>{key}</li>
  94. ))}
  95. </ul>
  96. </td>
  97. </tr>
  98. )
  99. })}
  100. </tbody>
  101. </table>
  102. </div>
  103. )
  104. }
  105. async function genLangs() {
  106. const langs_: Lang[] = []
  107. let en!: Lang
  108. const resources: Record<string, any> = {}
  109. // Initialize empty resource object
  110. for (const lang of LanguagesSupported)
  111. resources[lang] = await loadLangResources(lang)
  112. for (const [key, value] of Object.entries(resources)) {
  113. const keys = getNestedKeys(value.translation)
  114. const lang: Lang = {
  115. locale: key,
  116. keys: new Set(keys),
  117. count: keys.length,
  118. missing: [],
  119. extra: [],
  120. }
  121. langs_.push(lang)
  122. if (key === 'en-US') en = lang
  123. }
  124. for (const lang of langs_) {
  125. const missing: string[] = []
  126. const extra: string[] = []
  127. for (const key of lang.keys)
  128. if (!en.keys.has(key)) extra.push(key)
  129. for (const key of en.keys)
  130. if (!lang.keys.has(key)) missing.push(key)
  131. lang.missing = missing
  132. lang.extra = extra
  133. }
  134. return langs_
  135. }
  136. function getNestedKeys(translation: Record<string, any>): string[] {
  137. const nestedKeys: string[] = []
  138. const iterateKeys = (obj: Record<string, any>, prefix = '') => {
  139. for (const key in obj) {
  140. const nestedKey = prefix ? `${prefix}.${key}` : key
  141. // nestedKeys.push(nestedKey);
  142. if (typeof obj[key] === 'object') iterateKeys(obj[key], nestedKey)
  143. else if (typeof obj[key] === 'string') nestedKeys.push(nestedKey)
  144. }
  145. }
  146. iterateKeys(translation)
  147. return nestedKeys
  148. }
  149. type Lang = {
  150. locale: string;
  151. keys: Set<string>;
  152. count: number;
  153. missing: string[];
  154. extra: string[];
  155. }