translation-service.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /* globals window */
  6. /**
  7. * @module utils/translation-service
  8. */
  9. /* istanbul ignore else */
  10. if ( !window.CKEDITOR_TRANSLATIONS ) {
  11. window.CKEDITOR_TRANSLATIONS = {};
  12. }
  13. /**
  14. * Adds translations to existing ones.
  15. * These translations will later be available for the {@link module:utils/translation-service~translate `translate()`} function.
  16. *
  17. * add( 'pl', {
  18. * 'OK': 'OK',
  19. * 'Cancel [context: reject]': 'Anuluj'
  20. * } );
  21. *
  22. * If the message supports plural forms, make sure to provide an array with all plural forms:
  23. *
  24. * add( 'pl', {
  25. * 'Add editor': [ 'Dodaj edytor', 'Dodaj %0 edytory', 'Dodaj %0 edytorów' ]
  26. * } );
  27. *
  28. * If you cannot import this function from this module (e.g. because you use a CKEditor 5 build), then you can
  29. * still add translations by extending the global `window.CKEDITOR_TRANSLATIONS` object by using a function like
  30. * the one below:
  31. *
  32. * function addTranslations( language, translations ) {
  33. * if ( !window.CKEDITOR_TRANSLATIONS ) {
  34. * window.CKEDITOR_TRANSLATIONS = {};
  35. * }
  36. *
  37. * const dictionary = window.CKEDITOR_TRANSLATIONS[ language ] || ( window.CKEDITOR_TRANSLATIONS[ language ] = {} );
  38. *
  39. * // Extend the dictionary for the given language.
  40. * Object.assign( dictionary, translations );
  41. * }
  42. *
  43. * @param {String} language Target language.
  44. * @param {Object.<String, String>} translations Translations which will be added to the dictionary.
  45. * @param {Function} getPluralForm A function that returns the plural form index.
  46. */
  47. export function add( language, translations, getPluralForm ) {
  48. const languageTranslations = window.CKEDITOR_TRANSLATIONS[ language ] || ( window.CKEDITOR_TRANSLATIONS[ language ] = {} );
  49. languageTranslations.dictionary = languageTranslations.dictionary || {};
  50. languageTranslations.getPluralForm = getPluralForm || languageTranslations.getPluralForm || ( () => 0 );
  51. Object.assign( languageTranslations.dictionary, translations );
  52. }
  53. /**
  54. * Translates string if the translation of the string was previously added to the dictionary.
  55. * See {@link module:utils/translation-service Translation Service}.
  56. * This happens in a multi-language mode were translation modules are created by the bundler.
  57. *
  58. * When no translation is defined in the dictionary or the dictionary doesn't exist this function returns
  59. * the original string.
  60. *
  61. * In a single-language mode (when values passed to `t()` were replaced with target language strings) the dictionary
  62. * is left empty, so this function will return the original strings always.
  63. *
  64. * translate( 'pl', 'Cancel' );
  65. *
  66. * The third optional argument is the number of elements, based on which the single form or one of plural forms
  67. * should be picked when the message supports plural forms.
  68. *
  69. * translate( 'en', 'Add a space', 1 ); // 'Add %0 space'
  70. * translate( 'en', 'Add a space', 3 ); // 'Add %0 spaces'
  71. *
  72. * @param {String} language Target language.
  73. * @param {Object} message A message that will be translated.
  74. * @param {Number} [amount] A number of elements for which a plural form should be picked from the target language dictionary.
  75. * @returns {String} Translated sentence.
  76. */
  77. export function translate( language, message, amount = 1 ) {
  78. const numberOfLanguages = getNumberOfLanguages();
  79. if ( numberOfLanguages === 1 ) {
  80. // Override the language to the only supported one.
  81. // This can't be done in the `Locale` class, because the translations comes after the `Locale` class initialization.
  82. language = Object.keys( window.CKEDITOR_TRANSLATIONS )[ 0 ];
  83. }
  84. // TODO
  85. // const messageId = message.context ?
  86. // message.string + '_' + message.context :
  87. // message.string;
  88. const messageId = message.string;
  89. if ( numberOfLanguages === 0 || !hasTranslation( language, messageId ) ) {
  90. // return english forms:
  91. if ( amount !== 1 ) {
  92. return message.plural;
  93. }
  94. return message.string;
  95. }
  96. const dictionary = window.CKEDITOR_TRANSLATIONS[ language ].dictionary;
  97. const getPluralForm = window.CKEDITOR_TRANSLATIONS[ language ].getPluralForm;
  98. // TODO - maybe a warning could be helpful for some mismatches.
  99. if ( typeof dictionary[ messageId ] === 'string' ) {
  100. return dictionary[ messageId ];
  101. }
  102. // Note: The `translate` function is not responsible for replacing `%0, %1, ...` with values.
  103. return dictionary[ messageId ][ getPluralForm( amount ) ];
  104. }
  105. /**
  106. * Clears dictionaries for test purposes.
  107. *
  108. * @protected
  109. */
  110. export function _clear() {
  111. window.CKEDITOR_TRANSLATIONS = {};
  112. }
  113. // Checks whether the dictionary exists and translation in that dictionary exists.
  114. function hasTranslation( language, messageId ) {
  115. return (
  116. window.CKEDITOR_TRANSLATIONS[ language ] &&
  117. window.CKEDITOR_TRANSLATIONS[ language ].dictionary[ messageId ]
  118. );
  119. }
  120. function getNumberOfLanguages() {
  121. return Object.keys( window.CKEDITOR_TRANSLATIONS ).length;
  122. }