babel.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /**
  2. * Babel JavaScript Support
  3. *
  4. * Copyright (C) 2008 Edgewall Software
  5. * All rights reserved.
  6. *
  7. * This software is licensed as described in the file COPYING, which
  8. * you should have received as part of this distribution. The terms
  9. * are also available at http://babel.edgewall.org/wiki/License.
  10. *
  11. * This software consists of voluntary contributions made by many
  12. * individuals. For the exact contribution history, see the revision
  13. * history and logs, available at http://babel.edgewall.org/log/.
  14. */
  15. /**
  16. * A simple module that provides a gettext like translation interface.
  17. * The catalog passed to load() must be a object conforming to this
  18. * interface::
  19. *
  20. * {
  21. * messages: an object of {msgid: translations} items where
  22. * translations is an array of messages or a single
  23. * string if the message is not pluralizable.
  24. * plural_expr: the plural expression for the language.
  25. * locale: the identifier for this locale.
  26. * domain: the name of the domain.
  27. * }
  28. *
  29. * Missing elements in the object are ignored.
  30. *
  31. * Typical usage::
  32. *
  33. * var translations = babel.Translations.load(...).install();
  34. */
  35. var babel = new function() {
  36. var defaultPluralExpr = function(n) { return n == 1 ? 0 : 1; };
  37. var formatRegex = /%?%(?:\(([^\)]+)\))?([disr])/g;
  38. /**
  39. * A translations object implementing the gettext interface
  40. */
  41. var Translations = this.Translations = function(locale, domain) {
  42. this.messages = {};
  43. this.locale = locale || 'unknown';
  44. this.domain = domain || 'messages';
  45. this.pluralexpr = defaultPluralExpr;
  46. };
  47. /**
  48. * Create a new translations object from the catalog and return it.
  49. * See the babel-module comment for more details.
  50. */
  51. Translations.load = function(catalog) {
  52. var rv = new Translations();
  53. rv.load(catalog);
  54. return rv;
  55. };
  56. Translations.prototype = {
  57. /**
  58. * translate a single string.
  59. */
  60. gettext: function(string) {
  61. var translated = this.messages[string];
  62. if (typeof translated == 'undefined')
  63. return string;
  64. return (typeof translated == 'string') ? translated : translated[0];
  65. },
  66. /**
  67. * translate a pluralizable string
  68. */
  69. ngettext: function(singular, plural, n) {
  70. var translated = this.messages[singular];
  71. if (typeof translated == 'undefined')
  72. return (n == 1) ? singular : plural;
  73. return translated[this.pluralexpr(n)];
  74. },
  75. /**
  76. * Install this translation document wide. After this call, there are
  77. * three new methods on the window object: _, gettext and ngettext
  78. */
  79. install: function() {
  80. var self = this;
  81. window._ = window.gettext = function(string) {
  82. return self.gettext(string);
  83. };
  84. window.ngettext = function(singular, plural, n) {
  85. return self.ngettext(singular, plural, n);
  86. };
  87. return this;
  88. },
  89. /**
  90. * Works like Translations.load but updates the instance rather
  91. * then creating a new one.
  92. */
  93. load: function(catalog) {
  94. if (catalog.messages)
  95. this.update(catalog.messages)
  96. if (catalog.plural_expr)
  97. this.setPluralExpr(catalog.plural_expr);
  98. if (catalog.locale)
  99. this.locale = catalog.locale;
  100. if (catalog.domain)
  101. this.domain = catalog.domain;
  102. return this;
  103. },
  104. /**
  105. * Updates the translations with the object of messages.
  106. */
  107. update: function(mapping) {
  108. for (var key in mapping)
  109. if (mapping.hasOwnProperty(key))
  110. this.messages[key] = mapping[key];
  111. return this;
  112. },
  113. /**
  114. * Sets the plural expression
  115. */
  116. setPluralExpr: function(expr) {
  117. this.pluralexpr = new Function('n', 'return +(' + expr + ')');
  118. return this;
  119. }
  120. };
  121. /**
  122. * A python inspired string formatting function. Supports named and
  123. * positional placeholders and "s", "d" and "i" as type characters
  124. * without any formatting specifications.
  125. *
  126. * Examples::
  127. *
  128. * babel.format(_('Hello %s'), name)
  129. * babel.format(_('Progress: %(percent)s%%'), {percent: 100})
  130. */
  131. this.format = function() {
  132. var arg, string = arguments[0], idx = 0;
  133. if (arguments.length == 1)
  134. return string;
  135. else if (arguments.length == 2 && typeof arguments[1] == 'object')
  136. arg = arguments[1];
  137. else {
  138. arg = [];
  139. for (var i = 1, n = arguments.length; i != n; ++i)
  140. arg[i - 1] = arguments[i];
  141. }
  142. return string.replace(formatRegex, function(all, name, type) {
  143. if (all[0] == all[1]) return all.substring(1);
  144. var value = arg[name || idx++];
  145. return (type == 'i' || type == 'd') ? +value : value;
  146. });
  147. }
  148. };