i18n.spec.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { Trans, useTranslation } from 'react-i18next'
  2. describe('i18n', function () {
  3. describe('t', function () {
  4. it('translates a plain string', function () {
  5. const Test = () => {
  6. const { t } = useTranslation()
  7. return <div>{t('accept_change')}</div>
  8. }
  9. cy.mount(<Test />)
  10. cy.findByText('Accept change')
  11. })
  12. it('uses defaultValues', function () {
  13. const Test = () => {
  14. const { t } = useTranslation()
  15. return <div>{t('welcome_to_sl')}</div>
  16. }
  17. cy.mount(<Test />)
  18. cy.findByText('Welcome to Overleaf')
  19. })
  20. it('uses values', function () {
  21. const Test = () => {
  22. const { t } = useTranslation()
  23. return <div>{t('sort_by_x', { x: 'name' })}</div>
  24. }
  25. cy.mount(<Test />)
  26. cy.findByText('Sort by name')
  27. })
  28. })
  29. describe('Trans', function () {
  30. it('uses values', function () {
  31. const Test = () => {
  32. return (
  33. <div>
  34. <Trans
  35. i18nKey="sort_by_x"
  36. values={{ x: 'name' }}
  37. shouldUnescape
  38. tOptions={{ interpolation: { escapeValue: true } }}
  39. />
  40. </div>
  41. )
  42. }
  43. cy.mount(<Test />)
  44. cy.findByText('Sort by name')
  45. })
  46. it('uses an object of components', function () {
  47. const Test = () => {
  48. return (
  49. <div data-testid="container">
  50. <Trans
  51. i18nKey="in_order_to_match_institutional_metadata_associated"
  52. components={{ b: <b /> }}
  53. values={{ email: 'test@example.com' }}
  54. shouldUnescape
  55. tOptions={{ interpolation: { escapeValue: true } }}
  56. />
  57. </div>
  58. )
  59. }
  60. cy.mount(<Test />)
  61. cy.findByTestId('container')
  62. .should(
  63. 'have.text',
  64. 'In order to match your institutional metadata, your account is associated with the email test@example.com.'
  65. )
  66. .find('b')
  67. .should('have.length', 1)
  68. .should('have.text', 'test@example.com')
  69. })
  70. it('uses an array of components', function () {
  71. const Test = () => {
  72. return (
  73. <div data-testid="container">
  74. <Trans
  75. i18nKey="are_you_still_at"
  76. components={[<b />]} // eslint-disable-line react/jsx-key
  77. values={{ institutionName: 'Test' }}
  78. shouldUnescape
  79. tOptions={{ interpolation: { escapeValue: true } }}
  80. />
  81. </div>
  82. )
  83. }
  84. cy.mount(<Test />)
  85. cy.findByTestId('container')
  86. .should('have.text', 'Are you still at Test?')
  87. .find('b')
  88. .should('have.length', 1)
  89. .should('have.text', 'Test')
  90. })
  91. it('escapes special characters', function () {
  92. const Test = () => {
  93. return (
  94. <div data-testid="container">
  95. <Trans
  96. i18nKey="are_you_still_at"
  97. components={[<b />]} // eslint-disable-line react/jsx-key
  98. values={{ institutionName: "T&e's<code>t</code>ing" }}
  99. shouldUnescape
  100. tOptions={{ interpolation: { escapeValue: true } }}
  101. />
  102. </div>
  103. )
  104. }
  105. cy.mount(<Test />)
  106. cy.findByTestId('container')
  107. .should('have.text', "Are you still at T&e's<code>t</code>ing?")
  108. .find('b')
  109. .should('have.length', 1)
  110. .should('have.text', "T&e's<code>t</code>ing")
  111. })
  112. it('does not convert markup in values to components', function () {
  113. const Test = () => {
  114. return (
  115. <div data-testid="container">
  116. <Trans
  117. i18nKey="are_you_still_at"
  118. components={[<b />]} // eslint-disable-line react/jsx-key
  119. values={{
  120. institutionName: "<i>T</i>&<b>e</b>'s<code>t</code>ing",
  121. }}
  122. shouldUnescape
  123. tOptions={{ interpolation: { escapeValue: true } }}
  124. />
  125. </div>
  126. )
  127. }
  128. cy.mount(<Test />)
  129. cy.findByTestId('container')
  130. .should(
  131. 'have.text',
  132. "Are you still at <i>T</i>&<b>e</b>'s<code>t</code>ing?"
  133. )
  134. .find('b')
  135. .should('have.length', 1)
  136. .should('have.text', "<i>T</i>&<b>e</b>'s<code>t</code>ing")
  137. })
  138. })
  139. describe('plurals', function () {
  140. // See https://www.i18next.com/misc/json-format#i-18-next-json-v3
  141. it('picks singular for count=1', function () {
  142. const Test = () => {
  143. const { t } = useTranslation()
  144. return <div>{t('project_search_file_count', { count: 1 })}</div>
  145. }
  146. cy.mount(<Test />)
  147. cy.findByText('in 1 file')
  148. })
  149. it('picks plural for count>1', function () {
  150. const Test = () => {
  151. const { t } = useTranslation()
  152. return <div>{t('project_search_file_count', { count: 5 })}</div>
  153. }
  154. cy.mount(<Test />)
  155. cy.findByText('in 5 files')
  156. })
  157. it('picks plural for count=0', function () {
  158. const Test = () => {
  159. const { t } = useTranslation()
  160. return <div>{t('project_search_file_count', { count: 0 })}</div>
  161. }
  162. cy.mount(<Test />)
  163. cy.findByText('in 0 files')
  164. })
  165. })
  166. })