diff --git a/Gemfile b/Gemfile
index 04df2b0b7ca..4aa1e315536 100644
--- a/Gemfile
+++ b/Gemfile
@@ -89,6 +89,7 @@ gem "mini_sql"
gem "pry-rails", require: false
gem "pry-byebug", require: false
gem "rtlcss", require: false
+gem "messageformat-wrapper", require: false
gem "rake"
gem "thor", require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 0c6c51fe87a..9cee28dcb07 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -234,6 +234,8 @@ GEM
memory_profiler (1.0.2)
message_bus (4.3.8)
rack (>= 1.1.3)
+ messageformat-wrapper (1.0.0)
+ mini_racer (>= 0.6.3)
method_source (1.1.0)
mini_mime (1.1.5)
mini_racer (0.9.0)
@@ -646,6 +648,7 @@ DEPENDENCIES
maxminddb
memory_profiler
message_bus
+ messageformat-wrapper
mini_mime
mini_racer
mini_scheduler
@@ -728,4 +731,4 @@ DEPENDENCIES
yard
BUNDLED WITH
- 2.5.3
+ 2.5.9
diff --git a/app/assets/javascripts/discourse-i18n/package.json b/app/assets/javascripts/discourse-i18n/package.json
index d447e64e3d8..7456b248699 100644
--- a/app/assets/javascripts/discourse-i18n/package.json
+++ b/app/assets/javascripts/discourse-i18n/package.json
@@ -17,7 +17,8 @@
"src"
],
"dependencies": {
- "@embroider/addon-shim": "^1.8.9"
+ "@embroider/addon-shim": "^1.8.9",
+ "make-plural": "^7.4.0"
},
"engines": {
"node": ">= 18",
diff --git a/app/assets/javascripts/discourse-i18n/src/index.js b/app/assets/javascripts/discourse-i18n/src/index.js
index 650836ff59d..a9db4df2ff1 100644
--- a/app/assets/javascripts/discourse-i18n/src/index.js
+++ b/app/assets/javascripts/discourse-i18n/src/index.js
@@ -4,6 +4,8 @@ if (window.I18n) {
);
}
+import * as Cardinals from "make-plural/cardinals";
+
// The placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`.
const PLACEHOLDER = /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm;
const SEPARATOR = ".";
@@ -13,19 +15,14 @@ export class I18n {
defaultLocale = "en";
// Set current locale to null
- local = null;
+ locale = null;
fallbackLocale = null;
translations = null;
extras = null;
noFallbacks = false;
testing = false;
- // Set default pluralization rule
- pluralizationRules = {
- en(n) {
- return n === 0 ? ["zero", "none", "other"] : n === 1 ? "one" : "other";
- },
- };
+ pluralizationRules = Cardinals;
translate = (scope, options) => this._translate(scope, options);
@@ -36,6 +33,13 @@ export class I18n {
return this.locale || this.defaultLocale;
}
+ get pluralizationNormalizedLocale() {
+ if (this.currentLocale() === "pt") {
+ return "pt_PT";
+ }
+ return this.currentLocale().replace(/[_-].*/, "");
+ }
+
enableVerboseLocalization() {
let counter = 0;
let keys = {};
@@ -192,7 +196,9 @@ export class I18n {
options = this.prepareOptions(options);
let count = options.count.toString();
- let pluralizer = this.pluralizer(options.locale || this.currentLocale());
+ let pluralizer = this.pluralizer(
+ options.locale || this.pluralizationNormalizedLocale
+ );
let key = pluralizer(Math.abs(count));
let keys = typeof key === "object" && key instanceof Array ? key : [key];
let message = this.findAndTranslateValidNode(keys, translation);
@@ -371,6 +377,22 @@ export class I18n {
isValidNode(obj, node) {
return obj[node] !== null && obj[node] !== undefined;
}
+
+ messageFormat(key, options) {
+ const message = this._mfMessages.hasMessage(
+ key,
+ this._mfMessages.locale,
+ this._mfMessages.defaultLocale
+ );
+ if (!message) {
+ return "Missing Key: " + key;
+ }
+ try {
+ return this._mfMessages.get(key, options);
+ } catch (err) {
+ return err.message;
+ }
+ }
}
export class I18nMissingInterpolationArgument extends Error {
diff --git a/app/assets/javascripts/discourse/app/instance-initializers/localization.js b/app/assets/javascripts/discourse/app/instance-initializers/localization.js
index 8d07056e2bb..c482d1dca14 100644
--- a/app/assets/javascripts/discourse/app/instance-initializers/localization.js
+++ b/app/assets/javascripts/discourse/app/instance-initializers/localization.js
@@ -39,10 +39,5 @@ export default {
}
}
}
-
- for (let [key, value] of Object.entries(I18n._mfOverrides || {})) {
- key = key.replace(/^[a-z_]*js\./, "");
- I18n._compiledMFs[key] = value;
- }
},
};
diff --git a/app/assets/javascripts/discourse/app/loader-shims.js b/app/assets/javascripts/discourse/app/loader-shims.js
index f32488d69e9..035527ff118 100644
--- a/app/assets/javascripts/discourse/app/loader-shims.js
+++ b/app/assets/javascripts/discourse/app/loader-shims.js
@@ -64,3 +64,12 @@ loaderShim("truth-helpers/helpers/not", () =>
loaderShim("truth-helpers/helpers/or", () =>
importSync("truth-helpers/helpers/or")
);
+loaderShim("@messageformat/runtime/messages", () =>
+ importSync("@messageformat/runtime/messages")
+);
+loaderShim("@messageformat/runtime", () =>
+ importSync("@messageformat/runtime")
+);
+loaderShim("@messageformat/runtime/lib/cardinals", () =>
+ importSync("@messageformat/runtime/lib/cardinals")
+);
diff --git a/app/assets/javascripts/discourse/lib/translation-plugin.js b/app/assets/javascripts/discourse/lib/translation-plugin.js
index 0adbf39613c..ef626bdf1be 100644
--- a/app/assets/javascripts/discourse/lib/translation-plugin.js
+++ b/app/assets/javascripts/discourse/lib/translation-plugin.js
@@ -3,7 +3,7 @@ const Yaml = require("js-yaml");
const fs = require("fs");
const concat = require("broccoli-concat");
const mergeTrees = require("broccoli-merge-trees");
-const MessageFormat = require("messageformat");
+const MessageFormat = require("@messageformat/core");
const deepmerge = require("deepmerge");
const glob = require("glob");
const { shouldLoadPlugins } = require("discourse-plugins");
@@ -34,7 +34,7 @@ class TranslationPlugin extends Plugin {
} else if (key.endsWith("_MF")) {
// omit locale.js
let mfPath = subpath.slice(2).join(".");
- formats[mfPath] = this.mf.precompile(this.mf.parse(value));
+ formats[mfPath] = this.mf.compile(value);
}
});
}
@@ -74,11 +74,19 @@ class TranslationPlugin extends Plugin {
formats = Object.entries(formats).map(([k, v]) => `"${k}": ${v}`);
let contents = `
- I18n.locale = 'en';
- I18n.translations = ${JSON.stringify(parsed)};
- I18n.extras = ${JSON.stringify(extras)};
- MessageFormat = { locale: {} };
- I18n._compiledMFs = { ${formats.join(",\n")} };
+ (function() {
+ I18n.locale = 'en';
+ I18n.translations = ${JSON.stringify(parsed)};
+ I18n.extras = ${JSON.stringify(extras)};
+
+ const Messages = require("@messageformat/runtime/messages").default;
+ const { number, plural, select } = require("@messageformat/runtime");
+ const { en } = require("@messageformat/runtime/lib/cardinals");
+ const msgData = { en: { ${formats.join(",\n")} } };
+ const messages = new Messages(msgData, "en");
+ messages.defaultLocale = "en";
+ I18n._mfMessages = messages;
+ })()
`;
fs.writeFileSync(
@@ -110,7 +118,6 @@ module.exports.createI18nTree = function (discourseRoot, vendorJs) {
mergeTrees([
vendorJs,
discourseRoot + "/app/assets/javascripts/locales",
- discourseRoot + "/lib/javascripts",
en,
]),
{
@@ -118,17 +125,10 @@ module.exports.createI18nTree = function (discourseRoot, vendorJs) {
"i18n.js",
"moment.js",
"moment-timezone-with-data.js",
- "messageformat-lookup.js",
- "locale/en.js",
"client.en.js",
],
- headerFiles: [
- "i18n.js",
- "moment.js",
- "moment-timezone-with-data.js",
- "messageformat-lookup.js",
- ],
- footerFiles: ["client.en.js", "locale/en.js"],
+ headerFiles: ["i18n.js", "moment.js", "moment-timezone-with-data.js"],
+ footerFiles: ["client.en.js"],
outputFile: `assets/test-i18n.js`,
}
);
diff --git a/app/assets/javascripts/discourse/package.json b/app/assets/javascripts/discourse/package.json
index 924cc6dc419..5d47bdedc86 100644
--- a/app/assets/javascripts/discourse/package.json
+++ b/app/assets/javascripts/discourse/package.json
@@ -20,7 +20,9 @@
"@faker-js/faker": "^8.4.1",
"@glimmer/syntax": "^0.92.0",
"@highlightjs/cdn-assets": "^11.10.0",
+ "@messageformat/runtime": "^3.0.1",
"ace-builds": "^1.35.2",
+ "decorator-transforms": "^2.0.0",
"discourse-hbr": "1.0.0",
"discourse-widget-hbs": "1.0.0",
"ember-route-template": "^1.0.3",
@@ -29,8 +31,7 @@
"highlight.js": "^11.10.0",
"jspreadsheet-ce": "^4.13.4",
"morphlex": "^0.0.16",
- "pretty-text": "1.0.0",
- "decorator-transforms": "^2.0.0"
+ "pretty-text": "1.0.0"
},
"devDependencies": {
"@babel/core": "^7.24.7",
@@ -105,7 +106,6 @@
"js-yaml": "^4.1.0",
"loader.js": "^4.7.0",
"message-bus-client": "^4.3.8",
- "messageformat": "0.1.5",
"pretender": "^3.4.7",
"qunit": "^2.21.0",
"qunit-dom": "^3.2.0",
@@ -129,4 +129,4 @@
"ember": {
"edition": "octane"
}
-}
\ No newline at end of file
+}
diff --git a/app/assets/javascripts/discourse/tests/unit/lib/i18n-test.js b/app/assets/javascripts/discourse/tests/unit/lib/i18n-test.js
index 588fe01644c..a7314664fe7 100644
--- a/app/assets/javascripts/discourse/tests/unit/lib/i18n-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/lib/i18n-test.js
@@ -72,6 +72,13 @@ module("Unit | Utility | i18n", function (hooks) {
with_multiple_interpolate_arguments: "Hi %{username}, %{username2}",
},
},
+ ja: {
+ js: {
+ topic_stat_sentence_week: {
+ other: "先週、新しいトピックが %{count} 件投稿されました。",
+ },
+ },
+ },
};
// fake pluralization rules
@@ -172,18 +179,6 @@ module("Unit | Utility | i18n", function (hooks) {
},
},
};
- I18n.pluralizationRules.pl_PL = function (n) {
- if (n === 1) {
- return "one";
- }
- if (n % 10 >= 2 && n % 10 <= 4) {
- return "few";
- }
- if (n % 10 === 0) {
- return "many";
- }
- return "other";
- };
assert.strictEqual(
I18n.t("admin.dashboard.title"),
@@ -218,6 +213,20 @@ module("Unit | Utility | i18n", function (hooks) {
assert.strictEqual(I18n.t("word_count", { count: 3 }), "3 words");
assert.strictEqual(I18n.t("word_count", { count: 10 }), "10 words");
assert.strictEqual(I18n.t("word_count", { count: 100 }), "100 words");
+
+ I18n.locale = "ja";
+ assert.strictEqual(
+ I18n.t("topic_stat_sentence_week", { count: 0 }),
+ "先週、新しいトピックが 0 件投稿されました。"
+ );
+ assert.strictEqual(
+ I18n.t("topic_stat_sentence_week", { count: 1 }),
+ "先週、新しいトピックが 1 件投稿されました。"
+ );
+ assert.strictEqual(
+ I18n.t("topic_stat_sentence_week", { count: 2 }),
+ "先週、新しいトピックが 2 件投稿されました。"
+ );
});
test("adds the count to the missing translation strings", function (assert) {
@@ -323,4 +332,28 @@ module("Unit | Utility | i18n", function (hooks) {
I18n.testing = false;
}
});
+
+ test("pluralizationNormalizedLocale", function (assert) {
+ I18n.locale = "pt";
+
+ assert.strictEqual(
+ I18n.pluralizationNormalizedLocale,
+ "pt_PT",
+ "returns 'pt_PT' for the 'pt' locale, this is a special case of the 'make-plural' lib."
+ );
+
+ Object.entries({
+ pt_BR: "pt",
+ en_GB: "en",
+ bs_BA: "bs",
+ "fr-BE": "fr",
+ }).forEach(([raw, normalized]) => {
+ I18n.locale = raw;
+ assert.strictEqual(
+ I18n.pluralizationNormalizedLocale,
+ normalized,
+ `returns '${normalized}' for '${raw}'`
+ );
+ });
+ });
});
diff --git a/app/assets/javascripts/discourse/tests/unit/localization-test.js b/app/assets/javascripts/discourse/tests/unit/localization-test.js
index 153fcb62ac9..566f6c1a6b8 100644
--- a/app/assets/javascripts/discourse/tests/unit/localization-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/localization-test.js
@@ -10,9 +10,7 @@ module("initializer:localization", function (hooks) {
this._locale = I18n.locale;
this._translations = I18n.translations;
this._extras = I18n.extras;
- this._compiledMFs = I18n._compiledMFs;
this._overrides = I18n._overrides;
- this._mfOverrides = I18n._mfOverrides;
I18n.locale = "fr";
@@ -37,10 +35,6 @@ module("initializer:localization", function (hooks) {
},
};
- I18n._compiledMFs = {
- "user.messages.some_key_MF": () => "user.messages.some_key_MF (FR)",
- };
-
I18n.extras = {
fr: {
admin: {
@@ -67,9 +61,7 @@ module("initializer:localization", function (hooks) {
I18n.locale = this._locale;
I18n.translations = this._translations;
I18n.extras = this._extras;
- I18n._compiledMFs = this._compiledMFs;
I18n._overrides = this._overrides;
- I18n._mfOverrides = this._mfOverrides;
});
test("translation overrides", function (assert) {
@@ -159,21 +151,6 @@ module("initializer:localization", function (hooks) {
);
});
- test("translation overrides for MessageFormat strings", function (assert) {
- I18n._mfOverrides = {
- "js.user.messages.some_key_MF": () =>
- "user.messages.some_key_MF (FR override)",
- };
-
- LocalizationInitializer.initialize(this.owner);
-
- assert.strictEqual(
- I18n.messageFormat("user.messages.some_key_MF", {}),
- "user.messages.some_key_MF (FR override)",
- "overrides existing MessageFormat string"
- );
- });
-
test("skip translation override if parent node is not an object", function (assert) {
I18n._overrides = {
fr: {
diff --git a/app/controllers/extra_locales_controller.rb b/app/controllers/extra_locales_controller.rb
index 58c00f8a47d..7fb4fab9f64 100644
--- a/app/controllers/extra_locales_controller.rb
+++ b/app/controllers/extra_locales_controller.rb
@@ -11,6 +11,61 @@ class ExtraLocalesController < ApplicationController
OVERRIDES_BUNDLE ||= "overrides"
MD5_HASH_LENGTH ||= 32
+ MF_BUNDLE = "mf"
+ BUNDLES = [OVERRIDES_BUNDLE, MF_BUNDLE]
+
+ class << self
+ def js_digests
+ @js_digests ||= {}
+ end
+
+ def bundle_js_hash(bundle)
+ bundle_key = "#{bundle}_#{I18n.locale}"
+ if bundle.in?(BUNDLES)
+ site = RailsMultisite::ConnectionManagement.current_db
+
+ js_digests[site] ||= {}
+ js_digests[site][bundle_key] ||= begin
+ js = bundle_js(bundle)
+ js.present? ? Digest::MD5.hexdigest(js) : nil
+ end
+ else
+ js_digests[bundle_key] ||= Digest::MD5.hexdigest(bundle_js(bundle))
+ end
+ end
+
+ def url(bundle)
+ "#{Discourse.base_path}/extra-locales/#{bundle}?v=#{bundle_js_hash(bundle)}"
+ end
+
+ def client_overrides_exist?
+ bundle_js_hash(OVERRIDES_BUNDLE).present?
+ end
+
+ def bundle_js(bundle)
+ locale_str = I18n.locale.to_s
+ bundle_str = "#{bundle}_js"
+
+ case bundle
+ when OVERRIDES_BUNDLE
+ JsLocaleHelper.output_client_overrides(locale_str)
+ when MF_BUNDLE
+ JsLocaleHelper.output_MF(locale_str)
+ else
+ JsLocaleHelper.output_extra_locales(bundle_str, locale_str)
+ end
+ end
+
+ def bundle_js_with_hash(bundle)
+ js = bundle_js(bundle)
+ [js, Digest::MD5.hexdigest(js)]
+ end
+
+ def clear_cache!
+ site = RailsMultisite::ConnectionManagement.current_db
+ js_digests.delete(site)
+ end
+ end
def show
bundle = params[:bundle]
@@ -18,60 +73,18 @@ class ExtraLocalesController < ApplicationController
version = params[:v]
if version.present?
- if version.kind_of?(String) && version.length == MD5_HASH_LENGTH
- hash = ExtraLocalesController.bundle_js_hash(bundle)
- immutable_for(1.year) if hash == version
- else
- raise Discourse::InvalidParameters.new(:v)
- end
+ raise Discourse::InvalidParameters.new(:v) unless version.to_s.size == MD5_HASH_LENGTH
end
- render plain: ExtraLocalesController.bundle_js(bundle), content_type: "application/javascript"
- end
+ content, hash = ExtraLocalesController.bundle_js_with_hash(bundle)
+ immutable_for(1.year) if hash == version
- def self.bundle_js_hash(bundle)
- if bundle == OVERRIDES_BUNDLE
- site = RailsMultisite::ConnectionManagement.current_db
-
- @by_site ||= {}
- @by_site[site] ||= {}
- @by_site[site][I18n.locale] ||= begin
- js = bundle_js(bundle)
- js.present? ? Digest::MD5.hexdigest(js) : nil
- end
- else
- @bundle_js_hash ||= {}
- @bundle_js_hash["#{bundle}_#{I18n.locale}"] ||= Digest::MD5.hexdigest(bundle_js(bundle))
- end
- end
-
- def self.url(bundle)
- "#{Discourse.base_path}/extra-locales/#{bundle}?v=#{bundle_js_hash(bundle)}"
- end
-
- def self.client_overrides_exist?
- bundle_js_hash(OVERRIDES_BUNDLE).present?
- end
-
- def self.bundle_js(bundle)
- locale_str = I18n.locale.to_s
- bundle_str = "#{bundle}_js"
-
- if bundle == OVERRIDES_BUNDLE
- JsLocaleHelper.output_client_overrides(locale_str)
- else
- JsLocaleHelper.output_extra_locales(bundle_str, locale_str)
- end
- end
-
- def self.clear_cache!
- site = RailsMultisite::ConnectionManagement.current_db
- @by_site&.delete(site)
+ render plain: content, content_type: "application/javascript"
end
private
def valid_bundle?(bundle)
- bundle == OVERRIDES_BUNDLE || (bundle =~ /\A(admin|wizard)\z/ && current_user&.staff?)
+ bundle.in?(BUNDLES) || (bundle =~ /\A(admin|wizard)\z/ && current_user&.staff?)
end
end
diff --git a/app/models/translation_override.rb b/app/models/translation_override.rb
index d37e7301588..5862dc5dc7c 100644
--- a/app/models/translation_override.rb
+++ b/app/models/translation_override.rb
@@ -48,6 +48,14 @@ class TranslationOverride < ActiveRecord::Base
attribute :status, :integer
enum status: { up_to_date: 0, outdated: 1, invalid_interpolation_keys: 2, deprecated: 3 }
+ scope :mf_locales, ->(locale) { where(locale: locale).where("translation_key LIKE '%_MF'") }
+ scope :client_locales,
+ ->(locale) do
+ where(locale: locale)
+ .where("translation_key LIKE 'js.%' OR translation_key LIKE 'admin_js.%'")
+ .where.not("translation_key LIKE '%_MF'")
+ end
+
def self.upsert!(locale, key, value)
params = { locale: locale, translation_key: key }
@@ -58,10 +66,6 @@ class TranslationOverride < ActiveRecord::Base
I18n.overrides_disabled { I18n.t(transform_pluralized_key(key), locale: :en) }
data = { value: sanitized_value, original_translation: original_translation }
- if key.end_with?("_MF")
- _, filename = JsLocaleHelper.find_message_format_locale([locale], fallback_to_english: false)
- data[:compiled_js] = JsLocaleHelper.compile_message_format(filename, locale, sanitized_value)
- end
params.merge!(data) if translation_override.new_record?
i18n_changed(locale, [key]) if translation_override.update(data)
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 7051b794911..29465b6de57 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -27,7 +27,7 @@
<% add_resource_preload_list(script_asset_path("browser-update"), "script") %>
" as="script" nonce="<%= csp_nonce_placeholder %>">
" as="script" nonce="<%= csp_nonce_placeholder %>">
-
+
<%= preload_script 'browser-detect' %>
<%= preload_script "vendor" %>
@@ -37,8 +37,9 @@
<%- end %>
<%= preload_script "locales/#{I18n.locale}" %>
+ <%= preload_script_url ExtraLocalesController.url("mf") %>
<%- if ExtraLocalesController.client_overrides_exist? %>
- <%= preload_script_url ExtraLocalesController.url('overrides') %>
+ <%= preload_script_url ExtraLocalesController.url("overrides") %>
<%- end %>
<%- if staff? %>
diff --git a/app/views/qunit/theme.html.erb b/app/views/qunit/theme.html.erb
index 6e48425e4cb..cf6e86fdd5b 100644
--- a/app/views/qunit/theme.html.erb
+++ b/app/views/qunit/theme.html.erb
@@ -11,6 +11,7 @@
<%= preload_script "discourse" %>
<%= preload_script "test" %>
<%= preload_script "locales/#{I18n.locale}" %>
+ <%= preload_script_url ExtraLocalesController.url("mf") %>
<%= preload_script "admin" %>
<%- Discourse.find_plugin_js_assets(include_disabled: true).each do |file| %>
<%= preload_script file %>
@@ -50,7 +51,7 @@
<% elsif @suggested_themes %>
Theme QUnit Test Runner
- <%- if @suggested_themes.size == 0 %>
+ <%- if @suggested_themes.empty? %>
Cannot find any theme tests.
<%- else %>
Select a theme/component:
diff --git a/lib/discourse.rb b/lib/discourse.rb
index 774e065b298..c893f6ad9da 100644
--- a/lib/discourse.rb
+++ b/lib/discourse.rb
@@ -916,7 +916,6 @@ module Discourse
PrettyText.reset_context
DiscourseJsProcessor::Transpiler.reset_context if defined?(DiscourseJsProcessor::Transpiler)
- JsLocaleHelper.reset_context if defined?(JsLocaleHelper)
# warm up v8 after fork, that way we do not fork a v8 context
# it may cause issues if bg threads in a v8 isolate randomly stop
diff --git a/lib/javascripts/locale/af.js b/lib/javascripts/locale/af.js
deleted file mode 100644
index b03849f2d3a..00000000000
--- a/lib/javascripts/locale/af.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.af = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/am.js b/lib/javascripts/locale/am.js
deleted file mode 100644
index aa8af1d1a4f..00000000000
--- a/lib/javascripts/locale/am.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.am = function(n) {
- if (n === 0 || n == 1) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ar.js b/lib/javascripts/locale/ar.js
deleted file mode 100644
index d33d95ddba8..00000000000
--- a/lib/javascripts/locale/ar.js
+++ /dev/null
@@ -1,18 +0,0 @@
-MessageFormat.locale.ar = function(n) {
- if (n === 0) {
- return 'zero';
- }
- if (n == 1) {
- return 'one';
- }
- if (n == 2) {
- return 'two';
- }
- if ((n % 100) >= 3 && (n % 100) <= 10 && n == Math.floor(n)) {
- return 'few';
- }
- if ((n % 100) >= 11 && (n % 100) <= 99 && n == Math.floor(n)) {
- return 'many';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/be.js b/lib/javascripts/locale/be.js
deleted file mode 100644
index 5525d2d4339..00000000000
--- a/lib/javascripts/locale/be.js
+++ /dev/null
@@ -1,14 +0,0 @@
-MessageFormat.locale.be = function (n) {
- var r10 = n % 10, r100 = n % 100;
-
- if (r10 == 1 && r100 != 11)
- return 'one';
-
- if (r10 >= 2 && r10 <= 4 && (r100 < 12 || r100 > 14) && n == Math.floor(n))
- return 'few';
-
- if ((r10 == 0 || (r10 >= 5 && r10 <= 9) || (r100 >= 11 && r100 <= 14)) && n == Math.floor(n))
- return 'many';
-
- return 'other';
-};
diff --git a/lib/javascripts/locale/bg.js b/lib/javascripts/locale/bg.js
deleted file mode 100644
index 868baea07d5..00000000000
--- a/lib/javascripts/locale/bg.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.bg = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/bn.js b/lib/javascripts/locale/bn.js
deleted file mode 100644
index 1641ff32d6a..00000000000
--- a/lib/javascripts/locale/bn.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.bn = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/br.js b/lib/javascripts/locale/br.js
deleted file mode 100644
index 2e0d43fee1d..00000000000
--- a/lib/javascripts/locale/br.js
+++ /dev/null
@@ -1,18 +0,0 @@
-MessageFormat.locale.br = function (n) {
- if (n === 0) {
- return 'zero';
- }
- if (n == 1) {
- return 'one';
- }
- if (n == 2) {
- return 'two';
- }
- if (n == 3) {
- return 'few';
- }
- if (n == 6) {
- return 'many';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/bs.js b/lib/javascripts/locale/bs.js
deleted file mode 100644
index e2c139ddf96..00000000000
--- a/lib/javascripts/locale/bs.js
+++ /dev/null
@@ -1,10 +0,0 @@
-MessageFormat.locale.bs = function (n) {
- if ((n % 10) == 1 && (n % 100) != 11) {
- return 'one';
- }
- if ((n % 10) >= 2 && (n % 10) <= 4 &&
- ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ca.js b/lib/javascripts/locale/ca.js
deleted file mode 100644
index e2a685c674f..00000000000
--- a/lib/javascripts/locale/ca.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.ca = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/cs.js b/lib/javascripts/locale/cs.js
deleted file mode 100644
index 6a7f67ee230..00000000000
--- a/lib/javascripts/locale/cs.js
+++ /dev/null
@@ -1,9 +0,0 @@
-MessageFormat.locale.cs = function (n) {
- if (n == 1) {
- return 'one';
- }
- if (n == 2 || n == 3 || n == 4) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/cy.js b/lib/javascripts/locale/cy.js
deleted file mode 100644
index d98b1f49fa0..00000000000
--- a/lib/javascripts/locale/cy.js
+++ /dev/null
@@ -1,18 +0,0 @@
-MessageFormat.locale.cy = function (n) {
- if (n === 0) {
- return 'zero';
- }
- if (n == 1) {
- return 'one';
- }
- if (n == 2) {
- return 'two';
- }
- if (n == 3) {
- return 'few';
- }
- if (n == 6) {
- return 'many';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/da.js b/lib/javascripts/locale/da.js
deleted file mode 100644
index 7ea5765b295..00000000000
--- a/lib/javascripts/locale/da.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.da = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/de.js b/lib/javascripts/locale/de.js
deleted file mode 100644
index edca71c5400..00000000000
--- a/lib/javascripts/locale/de.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.de = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/el.js b/lib/javascripts/locale/el.js
deleted file mode 100644
index 8c5215a4442..00000000000
--- a/lib/javascripts/locale/el.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.el = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/en.js b/lib/javascripts/locale/en.js
deleted file mode 100644
index c2380b9bfe6..00000000000
--- a/lib/javascripts/locale/en.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.en = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/es.js b/lib/javascripts/locale/es.js
deleted file mode 100644
index 4397d10b8cc..00000000000
--- a/lib/javascripts/locale/es.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.es = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/et.js b/lib/javascripts/locale/et.js
deleted file mode 100644
index d4b7f5a3139..00000000000
--- a/lib/javascripts/locale/et.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.et = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/eu.js b/lib/javascripts/locale/eu.js
deleted file mode 100644
index 6da55df13e7..00000000000
--- a/lib/javascripts/locale/eu.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.eu = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/fa.js b/lib/javascripts/locale/fa.js
deleted file mode 100644
index 4280d1dabba..00000000000
--- a/lib/javascripts/locale/fa.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.fa = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/fa_IR.js b/lib/javascripts/locale/fa_IR.js
deleted file mode 100644
index a830b2cdc16..00000000000
--- a/lib/javascripts/locale/fa_IR.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.fa_IR = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/fi.js b/lib/javascripts/locale/fi.js
deleted file mode 100644
index 3315a840453..00000000000
--- a/lib/javascripts/locale/fi.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.fi = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/fil.js b/lib/javascripts/locale/fil.js
deleted file mode 100644
index af882daf477..00000000000
--- a/lib/javascripts/locale/fil.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.fil = function(n) {
- if (n === 0 || n == 1) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/fr.js b/lib/javascripts/locale/fr.js
deleted file mode 100644
index e562c3f8cb2..00000000000
--- a/lib/javascripts/locale/fr.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.fr = function (n) {
- if (n >= 0 && n < 2) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ga.js b/lib/javascripts/locale/ga.js
deleted file mode 100644
index c29aaadb66b..00000000000
--- a/lib/javascripts/locale/ga.js
+++ /dev/null
@@ -1,9 +0,0 @@
-MessageFormat.locale.ga = function (n) {
- if (n == 1) {
- return 'one';
- }
- if (n == 2) {
- return 'two';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/gl.js b/lib/javascripts/locale/gl.js
deleted file mode 100644
index 0d2a1b448c4..00000000000
--- a/lib/javascripts/locale/gl.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.gl = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/gsw.js b/lib/javascripts/locale/gsw.js
deleted file mode 100644
index 9aae2bcc8b6..00000000000
--- a/lib/javascripts/locale/gsw.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.gsw = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/gu.js b/lib/javascripts/locale/gu.js
deleted file mode 100644
index 70820dd9e0b..00000000000
--- a/lib/javascripts/locale/gu.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.gu = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/he.js b/lib/javascripts/locale/he.js
deleted file mode 100644
index bf828a5a7b3..00000000000
--- a/lib/javascripts/locale/he.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.he = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/hi.js b/lib/javascripts/locale/hi.js
deleted file mode 100644
index 68fac22a36d..00000000000
--- a/lib/javascripts/locale/hi.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.hi = function(n) {
- if (n === 0 || n == 1) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/hr.js b/lib/javascripts/locale/hr.js
deleted file mode 100644
index 815bf174ab9..00000000000
--- a/lib/javascripts/locale/hr.js
+++ /dev/null
@@ -1,10 +0,0 @@
-MessageFormat.locale.hr = function (n) {
- if ((n % 10) == 1 && (n % 100) != 11) {
- return 'one';
- }
- if ((n % 10) >= 2 && (n % 10) <= 4 &&
- ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/hu.js b/lib/javascripts/locale/hu.js
deleted file mode 100644
index 1fa3c21b26a..00000000000
--- a/lib/javascripts/locale/hu.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.hu = function(n) {
- return 'other';
-};
diff --git a/lib/javascripts/locale/hy.js b/lib/javascripts/locale/hy.js
deleted file mode 100644
index 7e05bb23718..00000000000
--- a/lib/javascripts/locale/hy.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.hy = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/id.js b/lib/javascripts/locale/id.js
deleted file mode 100644
index fb4b62bdee4..00000000000
--- a/lib/javascripts/locale/id.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.id = function(n) {
- return 'other';
-};
diff --git a/lib/javascripts/locale/in.js b/lib/javascripts/locale/in.js
deleted file mode 100644
index 95abe006a9e..00000000000
--- a/lib/javascripts/locale/in.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale["in"] = function(n) {
- return 'other';
-};
diff --git a/lib/javascripts/locale/is.js b/lib/javascripts/locale/is.js
deleted file mode 100644
index 48efd8f4636..00000000000
--- a/lib/javascripts/locale/is.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.is = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/it.js b/lib/javascripts/locale/it.js
deleted file mode 100644
index be964ccbe14..00000000000
--- a/lib/javascripts/locale/it.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.it = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/iw.js b/lib/javascripts/locale/iw.js
deleted file mode 100644
index a25fb2b170d..00000000000
--- a/lib/javascripts/locale/iw.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.iw = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/ja.js b/lib/javascripts/locale/ja.js
deleted file mode 100644
index a02267fa0a7..00000000000
--- a/lib/javascripts/locale/ja.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.ja = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/kn.js b/lib/javascripts/locale/kn.js
deleted file mode 100644
index 44c782db72f..00000000000
--- a/lib/javascripts/locale/kn.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.kn = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/ko.js b/lib/javascripts/locale/ko.js
deleted file mode 100644
index 899ffeae93c..00000000000
--- a/lib/javascripts/locale/ko.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.ko = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/lag.js b/lib/javascripts/locale/lag.js
deleted file mode 100644
index d4990b96c43..00000000000
--- a/lib/javascripts/locale/lag.js
+++ /dev/null
@@ -1,9 +0,0 @@
-MessageFormat.locale.lag = function (n) {
- if (n === 0) {
- return 'zero';
- }
- if (n > 0 && n < 2) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ln.js b/lib/javascripts/locale/ln.js
deleted file mode 100644
index 562e220b8cf..00000000000
--- a/lib/javascripts/locale/ln.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.ln = function(n) {
- if (n === 0 || n == 1) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/lt.js b/lib/javascripts/locale/lt.js
deleted file mode 100644
index 82878cfef9b..00000000000
--- a/lib/javascripts/locale/lt.js
+++ /dev/null
@@ -1,10 +0,0 @@
-MessageFormat.locale.lt = function (n) {
- if ((n % 10) == 1 && ((n % 100) < 11 || (n % 100) > 19)) {
- return 'one';
- }
- if ((n % 10) >= 2 && (n % 10) <= 9 &&
- ((n % 100) < 11 || (n % 100) > 19) && n == Math.floor(n)) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/lv.js b/lib/javascripts/locale/lv.js
deleted file mode 100644
index 75beb340148..00000000000
--- a/lib/javascripts/locale/lv.js
+++ /dev/null
@@ -1,9 +0,0 @@
-MessageFormat.locale.lv = function (n) {
- if (n === 0) {
- return 'zero';
- }
- if ((n % 10) == 1 && (n % 100) != 11) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/mk.js b/lib/javascripts/locale/mk.js
deleted file mode 100644
index c17aa2e3ad2..00000000000
--- a/lib/javascripts/locale/mk.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.mk = function (n) {
- if ((n % 10) == 1 && n != 11) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ml.js b/lib/javascripts/locale/ml.js
deleted file mode 100644
index f400a5f10cc..00000000000
--- a/lib/javascripts/locale/ml.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.ml = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/mo.js b/lib/javascripts/locale/mo.js
deleted file mode 100644
index 16d84d98f3e..00000000000
--- a/lib/javascripts/locale/mo.js
+++ /dev/null
@@ -1,10 +0,0 @@
-MessageFormat.locale.mo = function (n) {
- if (n == 1) {
- return 'one';
- }
- if (n === 0 || n != 1 && (n % 100) >= 1 &&
- (n % 100) <= 19 && n == Math.floor(n)) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/mr.js b/lib/javascripts/locale/mr.js
deleted file mode 100644
index da4d494ac19..00000000000
--- a/lib/javascripts/locale/mr.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.mr = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/ms.js b/lib/javascripts/locale/ms.js
deleted file mode 100644
index e635ae79aa4..00000000000
--- a/lib/javascripts/locale/ms.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.ms = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/mt.js b/lib/javascripts/locale/mt.js
deleted file mode 100644
index 6a071a7345d..00000000000
--- a/lib/javascripts/locale/mt.js
+++ /dev/null
@@ -1,12 +0,0 @@
-MessageFormat.locale.mt = function (n) {
- if (n == 1) {
- return 'one';
- }
- if (n === 0 || ((n % 100) >= 2 && (n % 100) <= 4 && n == Math.floor(n))) {
- return 'few';
- }
- if ((n % 100) >= 11 && (n % 100) <= 19 && n == Math.floor(n)) {
- return 'many';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/nl.js b/lib/javascripts/locale/nl.js
deleted file mode 100644
index 617e9071b04..00000000000
--- a/lib/javascripts/locale/nl.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.nl = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/no.js b/lib/javascripts/locale/no.js
deleted file mode 100644
index 025d3489d9b..00000000000
--- a/lib/javascripts/locale/no.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.no = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/or.js b/lib/javascripts/locale/or.js
deleted file mode 100644
index 04240a1859e..00000000000
--- a/lib/javascripts/locale/or.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.or = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/pl_PL.js b/lib/javascripts/locale/pl_PL.js
deleted file mode 100644
index 05437893163..00000000000
--- a/lib/javascripts/locale/pl_PL.js
+++ /dev/null
@@ -1,15 +0,0 @@
-MessageFormat.locale.pl_PL = function (n) {
- if (n == 1) {
- return 'one';
- }
- if ((n % 10) >= 2 && (n % 10) <= 4 &&
- ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
- return 'few';
- }
- if ((n % 10) === 0 || n != 1 && (n % 10) == 1 ||
- ((n % 10) >= 5 && (n % 10) <= 9 || (n % 100) >= 12 && (n % 100) <= 14) &&
- n == Math.floor(n)) {
- return 'many';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/pt.js b/lib/javascripts/locale/pt.js
deleted file mode 100644
index a4b65eb906d..00000000000
--- a/lib/javascripts/locale/pt.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.pt = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/pt_BR.js b/lib/javascripts/locale/pt_BR.js
deleted file mode 100644
index c2c797c490c..00000000000
--- a/lib/javascripts/locale/pt_BR.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.pt_BR = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/ro.js b/lib/javascripts/locale/ro.js
deleted file mode 100644
index 26453eadd6e..00000000000
--- a/lib/javascripts/locale/ro.js
+++ /dev/null
@@ -1,10 +0,0 @@
-MessageFormat.locale.ro = function (n) {
- if (n == 1) {
- return 'one';
- }
- if (n === 0 || n != 1 && (n % 100) >= 1 &&
- (n % 100) <= 19 && n == Math.floor(n)) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ru.js b/lib/javascripts/locale/ru.js
deleted file mode 100644
index 09d2ed9cb68..00000000000
--- a/lib/javascripts/locale/ru.js
+++ /dev/null
@@ -1,16 +0,0 @@
-MessageFormat.locale.ru = function (n) {
- var r10 = n % 10, r100 = n % 100;
-
- if (r10 == 1 && r100 != 11)
- return 'one';
-
- if (r10 >= 2 && r10 <= 4 && (r100 < 12 || r100 > 14) && n == Math.floor(n))
- return 'few';
-
- if (r10 === 0 || (r10 >= 5 && r10 <= 9) ||
- (r100 >= 11 && r100 <= 14) && n == Math.floor(n)) {
- return 'many';
- }
-
- return 'other';
-};
diff --git a/lib/javascripts/locale/shi.js b/lib/javascripts/locale/shi.js
deleted file mode 100644
index 9e86dca14a6..00000000000
--- a/lib/javascripts/locale/shi.js
+++ /dev/null
@@ -1,9 +0,0 @@
-MessageFormat.locale.shi = function(n) {
- if (n >= 0 && n <= 1) {
- return 'one';
- }
- if (n >= 2 && n <= 10 && n == Math.floor(n)) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/sk.js b/lib/javascripts/locale/sk.js
deleted file mode 100644
index ef041ddb236..00000000000
--- a/lib/javascripts/locale/sk.js
+++ /dev/null
@@ -1,9 +0,0 @@
-MessageFormat.locale.sk = function (n) {
- if (n == 1) {
- return 'one';
- }
- if (n == 2 || n == 3 || n == 4) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/sl.js b/lib/javascripts/locale/sl.js
deleted file mode 100644
index 7dd591b01e9..00000000000
--- a/lib/javascripts/locale/sl.js
+++ /dev/null
@@ -1,12 +0,0 @@
-MessageFormat.locale.sl = function (n) {
- if ((n % 100) == 1) {
- return 'one';
- }
- if ((n % 100) == 2) {
- return 'two';
- }
- if ((n % 100) == 3 || (n % 100) == 4) {
- return 'few';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/sq.js b/lib/javascripts/locale/sq.js
deleted file mode 100644
index 1e683894f21..00000000000
--- a/lib/javascripts/locale/sq.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.sq = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/sr.js b/lib/javascripts/locale/sr.js
deleted file mode 100644
index 9bd86634529..00000000000
--- a/lib/javascripts/locale/sr.js
+++ /dev/null
@@ -1,11 +0,0 @@
-MessageFormat.locale.sr = function (n) {
- var r10 = n % 10, r100 = n % 100;
-
- if (r10 == 1 && r100 != 11)
- return 'one';
-
- if (r10 >= 2 && r10 <= 4 && (r100 < 12 || r100 > 14) && n == Math.floor(n))
- return 'few';
-
- return 'other';
-};
\ No newline at end of file
diff --git a/lib/javascripts/locale/sv.js b/lib/javascripts/locale/sv.js
deleted file mode 100644
index e566a339d12..00000000000
--- a/lib/javascripts/locale/sv.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.sv = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/sw.js b/lib/javascripts/locale/sw.js
deleted file mode 100644
index 7dd56c146fe..00000000000
--- a/lib/javascripts/locale/sw.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.sw = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/ta.js b/lib/javascripts/locale/ta.js
deleted file mode 100644
index 08a4ae01fdf..00000000000
--- a/lib/javascripts/locale/ta.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.ta = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/te.js b/lib/javascripts/locale/te.js
deleted file mode 100644
index 61ccb27f6c5..00000000000
--- a/lib/javascripts/locale/te.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.te = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/th.js b/lib/javascripts/locale/th.js
deleted file mode 100644
index 9ef170824d8..00000000000
--- a/lib/javascripts/locale/th.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.th = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/tl.js b/lib/javascripts/locale/tl.js
deleted file mode 100644
index bc68843df27..00000000000
--- a/lib/javascripts/locale/tl.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.tl = function(n) {
- if (n === 0 || n == 1) {
- return 'one';
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/tr.js b/lib/javascripts/locale/tr.js
deleted file mode 100644
index 438941ad9b4..00000000000
--- a/lib/javascripts/locale/tr.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.tr = function(n) {
- return 'other';
-};
diff --git a/lib/javascripts/locale/tr_TR.js b/lib/javascripts/locale/tr_TR.js
deleted file mode 100644
index 83d9c02c960..00000000000
--- a/lib/javascripts/locale/tr_TR.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.tr_TR = function(n) {
- return 'other';
-};
diff --git a/lib/javascripts/locale/ug.js b/lib/javascripts/locale/ug.js
deleted file mode 100644
index bcf429ee076..00000000000
--- a/lib/javascripts/locale/ug.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.ug = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/uk.js b/lib/javascripts/locale/uk.js
deleted file mode 100644
index edb43885ab4..00000000000
--- a/lib/javascripts/locale/uk.js
+++ /dev/null
@@ -1,15 +0,0 @@
-MessageFormat.locale.uk = function (n) {
- if ((n % 10) == 1 && (n % 100) != 11) {
- return 'one';
- }
- if ((n % 10) >= 2 && (n % 10) <= 4 &&
- ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
- return 'few';
- }
- if ((n % 10) === 0 || ((n % 10) >= 5 && (n % 10) <= 9) ||
- ((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) {
- // return 'many';
- return 'other'; // TODO should be "many" but is not defined in translations
- }
- return 'other';
-};
diff --git a/lib/javascripts/locale/ur.js b/lib/javascripts/locale/ur.js
deleted file mode 100644
index 5a875c9e372..00000000000
--- a/lib/javascripts/locale/ur.js
+++ /dev/null
@@ -1,6 +0,0 @@
-MessageFormat.locale.ur = function ( n ) {
- if ( n === 1 ) {
- return "one";
- }
- return "other";
-};
diff --git a/lib/javascripts/locale/vi.js b/lib/javascripts/locale/vi.js
deleted file mode 100644
index 8a5b74698ca..00000000000
--- a/lib/javascripts/locale/vi.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.vi = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/zh.js b/lib/javascripts/locale/zh.js
deleted file mode 100644
index 6ae270cf660..00000000000
--- a/lib/javascripts/locale/zh.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.zh = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/zh_CN.js b/lib/javascripts/locale/zh_CN.js
deleted file mode 100644
index ef595cb377d..00000000000
--- a/lib/javascripts/locale/zh_CN.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.zh_CN = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/locale/zh_TW.js b/lib/javascripts/locale/zh_TW.js
deleted file mode 100644
index 3f4bf033904..00000000000
--- a/lib/javascripts/locale/zh_TW.js
+++ /dev/null
@@ -1,3 +0,0 @@
-MessageFormat.locale.zh_TW = function ( n ) {
- return "other";
-};
diff --git a/lib/javascripts/messageformat-lookup.js b/lib/javascripts/messageformat-lookup.js
deleted file mode 100644
index eecb5e22bc3..00000000000
--- a/lib/javascripts/messageformat-lookup.js
+++ /dev/null
@@ -1,14 +0,0 @@
-(function () {
- I18n.messageFormat = function (key, options) {
- var fn = I18n._compiledMFs[key];
- if (fn) {
- try {
- return fn(options);
- } catch (err) {
- return err.message;
- }
- } else {
- return "Missing Key: " + key;
- }
- };
-})();
diff --git a/lib/js_locale_helper.rb b/lib/js_locale_helper.rb
index b3ac0f695b4..771984e318d 100644
--- a/lib/js_locale_helper.rb
+++ b/lib/js_locale_helper.rb
@@ -117,14 +117,14 @@ module JsLocaleHelper
@loaded_merges = nil
end
- def self.translations_for(locale_str)
+ def self.translations_for(locale_str, no_fallback: false)
clear_cache! if Rails.env.development?
locale_sym = locale_str.to_sym
translations =
I18n.with_locale(locale_sym) do
- if locale_sym == :en
+ if locale_sym == :en || no_fallback
load_translations(locale_sym)
else
load_translations_merged(*I18n.fallbacks[locale_sym])
@@ -134,14 +134,43 @@ module JsLocaleHelper
Marshal.load(Marshal.dump(translations))
end
+ def self.output_MF(locale)
+ require "messageformat"
+
+ message_formats =
+ I18n.fallbacks[locale]
+ .each_with_object({}) do |l, hash|
+ translations = translations_for(l, no_fallback: true)
+ hash[l.to_s.dasherize] = remove_message_formats!(translations, l).merge(
+ TranslationOverride
+ .mf_locales(l)
+ .pluck(:translation_key, :value)
+ .to_h
+ .transform_keys { _1.sub(/^[a-z_]*js\./, "") },
+ )
+ end
+ .compact_blank
+ compiled = MessageFormat.compile(message_formats.keys, message_formats)
+ transpiled = DiscourseJsProcessor.transpile(<<~JS, "", "discourse-mf")
+ import Messages from '@messageformat/runtime/messages';
+ #{compiled.sub("export default", "const msgData =")};
+ const messages = new Messages(msgData, "#{locale.to_s.dasherize}");
+ messages.defaultLocale = "en";
+ globalThis.I18n._mfMessages = messages;
+ JS
+ <<~JS
+ #{transpiled}
+ require("discourse-mf");
+ JS
+ end
+
def self.output_locale(locale)
locale_str = locale.to_s
fallback_locale_str = LocaleSiteSetting.fallback_locale(locale_str)&.to_s
translations = translations_for(locale_str)
- message_formats = remove_message_formats!(translations, locale)
- mf_locale, mf_filename = find_message_format_locale([locale_str], fallback_to_english: true)
- result = generate_message_format(message_formats, mf_locale, mf_filename)
+ remove_message_formats!(translations, locale)
+ result = +""
translations.keys.each do |l|
translations[l].keys.each { |k| translations[l].delete(k) unless k == "js" }
@@ -153,9 +182,6 @@ module JsLocaleHelper
if fallback_locale_str && fallback_locale_str != "en"
result << "I18n.fallbackLocale = '#{fallback_locale_str}';\n"
end
- if mf_locale != "en"
- result << "I18n.pluralizationRules.#{locale_str} = MessageFormat.locale.#{mf_locale};\n"
- end
# moment
result << File.read("#{Rails.root}/vendor/assets/javascripts/moment.js")
@@ -168,44 +194,24 @@ module JsLocaleHelper
end
def self.output_client_overrides(main_locale)
- all_overrides = {}
- has_overrides = false
-
- I18n.fallbacks[main_locale].each do |locale|
- overrides =
- all_overrides[locale] = TranslationOverride
- .where(locale: locale)
- .where("translation_key LIKE 'js.%' OR translation_key LIKE 'admin_js.%'")
- .pluck(:translation_key, :value, :compiled_js)
-
- has_overrides ||= overrides.present?
- end
-
- return "" if !has_overrides
-
- result = +"I18n._overrides = {};"
- existing_keys = Set.new
- message_formats = []
-
- all_overrides.each do |locale, overrides|
- translations = {}
-
- overrides.each do |key, value, compiled_js|
- next if existing_keys.include?(key)
- existing_keys << key
-
- if key.end_with?("_MF")
- message_formats << "#{key.inspect}: #{compiled_js}"
- else
- translations[key] = value
+ locales = I18n.fallbacks[main_locale]
+ all_overrides =
+ locales
+ .each_with_object({}) do |locale, overrides|
+ overrides[locale] = TranslationOverride
+ .client_locales(locale)
+ .pluck(:translation_key, :value)
+ .to_h
end
- end
+ .compact_blank
- result << "I18n._overrides['#{locale}'] = #{translations.to_json};" if translations.present?
+ return "" if all_overrides.blank?
+
+ all_overrides.reduce do |(_, main_overrides), (_, fallback_overrides)|
+ fallback_overrides.slice!(*fallback_overrides.keys - main_overrides.keys)
end
- result << "I18n._mfOverrides = {#{message_formats.join(", ")}};"
- result
+ "I18n._overrides = #{all_overrides.compact_blank.to_json};"
end
def self.output_extra_locales(bundle, locale)
@@ -251,11 +257,6 @@ module JsLocaleHelper
end
end
- def self.find_message_format_locale(locale_chain, fallback_to_english:)
- path = "#{Rails.root}/lib/javascripts/locale"
- find_locale(locale_chain, path, :message_format, fallback_to_english: fallback_to_english)
- end
-
def self.find_locale(locale_chain, path, type, fallback_to_english:)
locale_chain.map!(&:to_s)
@@ -301,55 +302,6 @@ module JsLocaleHelper
filename && File.exist?(filename) ? File.read(filename) << "\n" : ""
end
- def self.generate_message_format(message_formats, locale, filename)
- formats =
- message_formats
- .map { |k, v| k.inspect << " : " << compile_message_format(filename, locale, v) }
- .join(", ")
-
- result = +"MessageFormat = {locale: {}};\n"
- result << "I18n._compiledMFs = {#{formats}};\n"
- result << File.read(filename) << "\n"
-
- if locale != "en"
- # Include "en" pluralization rules for use in fallbacks
- _, en_filename = find_message_format_locale(["en"], fallback_to_english: false)
- result << File.read(en_filename) << "\n"
- end
-
- result << File.read("#{Rails.root}/lib/javascripts/messageformat-lookup.js") << "\n"
- end
-
- def self.reset_context
- @ctx&.dispose
- @ctx = nil
- end
-
- @mutex = Mutex.new
- def self.with_context
- @mutex.synchronize do
- yield(
- @ctx ||=
- begin
- ctx = MiniRacer::Context.new(timeout: 15_000, ensure_gc_after_idle: 2000)
- ctx.load("#{Rails.root}/node_modules/messageformat/messageformat.js")
- ctx
- end
- )
- end
- end
-
- def self.compile_message_format(path, locale, format)
- with_context do |ctx|
- ctx.load(path) if File.exist?(path)
- ctx.eval("mf = new MessageFormat('#{locale}');")
- ctx.eval("mf.precompile(mf.parse(#{format.inspect}))")
- end
- rescue MiniRacer::EvalError => e
- message = +"Invalid Format: " << e.message
- "function(){ return #{message.inspect};}"
- end
-
def self.remove_message_formats!(translations, locale)
message_formats = {}
I18n.fallbacks[locale]
diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb
index faf830d00b5..058248f1241 100644
--- a/lib/plugin/instance.rb
+++ b/lib/plugin/instance.rb
@@ -1275,13 +1275,6 @@ class Plugin::Instance
locale_chain = opts[:fallbackLocale] ? [locale, opts[:fallbackLocale]] : [locale]
lib_locale_path = File.join(root_path, "lib/javascripts/locale")
- path = File.join(lib_locale_path, "message_format")
- opts[:message_format] = find_locale_file(locale_chain, path)
- opts[:message_format] = JsLocaleHelper.find_message_format_locale(
- locale_chain,
- fallback_to_english: false,
- ) unless opts[:message_format]
-
path = File.join(lib_locale_path, "moment_js")
opts[:moment_js] = find_locale_file(locale_chain, path)
opts[:moment_js] = JsLocaleHelper.find_moment_locale(locale_chain) unless opts[:moment_js]
@@ -1357,8 +1350,7 @@ class Plugin::Instance
def valid_locale?(custom_locale)
File.exist?(custom_locale[:client_locale_file]) &&
File.exist?(custom_locale[:server_locale_file]) &&
- File.exist?(custom_locale[:js_locale_file]) && custom_locale[:message_format] &&
- custom_locale[:moment_js]
+ File.exist?(custom_locale[:js_locale_file]) && custom_locale[:moment_js]
end
def find_locale_file(locale_chain, path)
diff --git a/lib/tasks/qunit.rake b/lib/tasks/qunit.rake
index c8fe5bf123b..9066958c018 100644
--- a/lib/tasks/qunit.rake
+++ b/lib/tasks/qunit.rake
@@ -55,7 +55,6 @@ task "qunit:test", %i[timeout qunit_path filter] do |_, args|
begin
success = true
- test_path = "#{Rails.root}/test"
qunit_path = args[:qunit_path]
filter = args[:filter]
diff --git a/package.json b/package.json
index 91f7c578436..c79b6e613c3 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"@glint/environment-ember-template-imports": "^1.4.0",
"@glint/template": "^1.4.0",
"@json-editor/json-editor": "2.10.0",
+ "@messageformat/core": "^3.3.0",
"@mixer/parallel-prettier": "^2.0.3",
"chart.js": "3.5.1",
"chartjs-plugin-datalabels": "2.2.0",
@@ -83,4 +84,4 @@
"npm": "please-use-yarn",
"yarn": ">= 1.21.1"
}
-}
\ No newline at end of file
+}
diff --git a/spec/lib/js_locale_helper_spec.rb b/spec/lib/js_locale_helper_spec.rb
index 7d3cd760232..24bf9ec746a 100644
--- a/spec/lib/js_locale_helper_spec.rb
+++ b/spec/lib/js_locale_helper_spec.rb
@@ -5,23 +5,27 @@ require "mini_racer"
RSpec.describe JsLocaleHelper do
let(:v8_ctx) do
node_modules = "#{Rails.root}/node_modules/"
-
transpiler = DiscourseJsProcessor::Transpiler.new
- discourse_i18n =
- transpiler.perform(
- File.read("#{Rails.root}/app/assets/javascripts/discourse-i18n/src/index.js"),
- "app/assets/javascripts/discourse",
- "discourse-i18n",
- )
-
ctx = MiniRacer::Context.new
ctx.load("#{node_modules}/loader.js/dist/loader/loader.js")
ctx.eval("var window = globalThis;")
- ctx.eval(discourse_i18n)
+ {
+ "@messageformat/runtime/messages": "#{node_modules}/@messageformat/runtime/esm/messages.js",
+ "@messageformat/runtime": "#{node_modules}/@messageformat/runtime/esm/runtime.js",
+ "@messageformat/runtime/lib/cardinals":
+ "#{node_modules}/@messageformat/runtime/esm/cardinals.js",
+ "make-plural/cardinals": "#{node_modules}/make-plural/cardinals.mjs",
+ "discourse-i18n": "#{Rails.root}/app/assets/javascripts/discourse-i18n/src/index.js",
+ }.each do |module_name, path|
+ ctx.eval(transpiler.perform(File.read(path), "", module_name.to_s))
+ end
ctx.eval <<~JS
define("discourse/loader-shims", () => {})
JS
- ctx.load("#{Rails.root}/app/assets/javascripts/locales/i18n.js")
+ # As there are circular references in the return value, this raises an
+ # error if we let MiniRacer try to convert the value to JSON. Forcing
+ # returning `null` from `#eval` will prevent that.
+ ctx.eval("#{File.read("#{Rails.root}/app/assets/javascripts/locales/i18n.js")};null")
ctx
end
@@ -54,116 +58,6 @@ RSpec.describe JsLocaleHelper do
end
end
- describe "message format" do
- def message_format_filename(locale)
- Rails.root + "lib/javascripts/locale/#{locale}.js"
- end
-
- def setup_message_format(format)
- filename = message_format_filename("en")
- compiled = JsLocaleHelper.compile_message_format(filename, "en", format)
-
- @ctx = MiniRacer::Context.new
- @ctx.eval("MessageFormat = {locale: {}};")
- @ctx.load(filename)
- @ctx.eval("var test = #{compiled}")
- end
-
- def localize(opts)
- @ctx.eval("test(#{opts.to_json})")
- end
-
- it "handles plurals" do
- setup_message_format(
- "{NUM_RESULTS, plural,
- one {1 result}
- other {# results}
- }",
- )
- expect(localize(NUM_RESULTS: 1)).to eq("1 result")
- expect(localize(NUM_RESULTS: 2)).to eq("2 results")
- end
-
- it "handles double plurals" do
- setup_message_format(
- "{NUM_RESULTS, plural,
- one {1 result}
- other {# results}
- } and {NUM_APPLES, plural,
- one {1 apple}
- other {# apples}
- }",
- )
-
- expect(localize(NUM_RESULTS: 1, NUM_APPLES: 2)).to eq("1 result and 2 apples")
- expect(localize(NUM_RESULTS: 2, NUM_APPLES: 1)).to eq("2 results and 1 apple")
- end
-
- it "handles select" do
- setup_message_format("{GENDER, select, male {He} female {She} other {They}} read a book")
- expect(localize(GENDER: "male")).to eq("He read a book")
- expect(localize(GENDER: "female")).to eq("She read a book")
- expect(localize(GENDER: "none")).to eq("They read a book")
- end
-
- it "can strip out message formats" do
- hash = { "a" => "b", "c" => { "d" => { "f_MF" => "bob" } } }
- expect(JsLocaleHelper.strip_out_message_formats!(hash)).to eq("c.d.f_MF" => "bob")
- expect(hash["c"]["d"]).to eq({})
- end
-
- it "handles message format special keys" do
- JsLocaleHelper.set_translations(
- "en",
- "en" => {
- "js" => {
- "hello" => "world",
- "test_MF" => "{HELLO} {COUNT, plural, one {1 duck} other {# ducks}}",
- "error_MF" => "{{BLA}",
- "simple_MF" => "{COUNT, plural, one {1} other {#}}",
- },
- "admin_js" => {
- "foo_MF" => "{HELLO} {COUNT, plural, one {1 duck} other {# ducks}}",
- },
- },
- )
-
- ctx = MiniRacer::Context.new
- ctx.eval("I18n = { pluralizationRules: {} };")
- ctx.eval(JsLocaleHelper.output_locale("en"))
-
- expect(ctx.eval('I18n.translations["en"]["js"]["hello"]')).to eq("world")
- expect(ctx.eval('I18n.translations["en"]["js"]["test_MF"]')).to eq(nil)
-
- expect(ctx.eval('I18n.messageFormat("test_MF", { HELLO: "hi", COUNT: 3 })')).to eq(
- "hi 3 ducks",
- )
- expect(ctx.eval('I18n.messageFormat("error_MF", { HELLO: "hi", COUNT: 3 })')).to match(
- /Invalid Format/,
- )
- expect(ctx.eval('I18n.messageFormat("missing", {})')).to match(/missing/)
- expect(ctx.eval('I18n.messageFormat("simple_MF", {})')).to match(/COUNT/) # error
- expect(ctx.eval('I18n.messageFormat("foo_MF", { HELLO: "hi", COUNT: 4 })')).to eq(
- "hi 4 ducks",
- )
- end
-
- it "load pluralization rules before precompile" do
- message = JsLocaleHelper.compile_message_format(message_format_filename("ru"), "ru", "format")
- expect(message).not_to match "Plural Function not found"
- end
-
- it "uses message formats from fallback locale" do
- translations = JsLocaleHelper.translations_for(:en_GB)
- en_gb_message_formats = JsLocaleHelper.remove_message_formats!(translations, :en_GB)
- expect(en_gb_message_formats).to_not be_empty
-
- translations = JsLocaleHelper.translations_for(:en)
- en_message_formats = JsLocaleHelper.remove_message_formats!(translations, :en)
- expect(en_gb_message_formats).to eq(en_message_formats)
- end
- end
-
it "performs fallbacks to English if a translation is not available" do
JsLocaleHelper.set_translations(
"en",
@@ -235,35 +129,6 @@ RSpec.describe JsLocaleHelper do
end
end
- it "correctly evaluates message formats in en fallback" do
- allow_missing_translations do
- JsLocaleHelper.set_translations("en", "en" => { "js" => { "something_MF" => "en mf" } })
- JsLocaleHelper.set_translations("de", "de" => { "js" => { "something_MF" => "de mf" } })
-
- TranslationOverride.upsert!("en", "js.something_MF", <<~MF.strip)
- There {
- UNREAD, plural,
- =0 {are no}
- one {is one unread}
- other {are # unread}
- }
- MF
-
- v8_ctx.eval(JsLocaleHelper.output_locale("de"))
- v8_ctx.eval(JsLocaleHelper.output_client_overrides("de"))
- v8_ctx.eval(<<~JS)
- for (let [key, value] of Object.entries(I18n._mfOverrides || {})) {
- key = key.replace(/^[a-z_]*js\./, "");
- I18n._compiledMFs[key] = value;
- }
- JS
-
- expect(v8_ctx.eval("I18n.messageFormat('something_MF', { UNREAD: 1 })")).to eq(
- "There is one unread",
- )
- end
- end
-
LocaleSiteSetting.values.each do |locale|
it "generates valid date helpers for #{locale[:value]} locale" do
js = JsLocaleHelper.output_locale(locale[:value])
@@ -281,31 +146,86 @@ RSpec.describe JsLocaleHelper do
end
end
- describe ".find_message_format_locale" do
- it "finds locale's message format rules" do
- locale, filename =
- JsLocaleHelper.find_message_format_locale([:de], fallback_to_english: false)
- expect(locale).to eq("de")
- expect(filename).to end_with("/de.js")
+ describe ".output_MF" do
+ let(:output) { described_class.output_MF(locale).gsub(/^import.*$/, "") }
+ let(:generated_locales) { v8_ctx.eval("Object.keys(I18n._mfMessages._data)") }
+ let(:translated_message) do
+ v8_ctx.eval("I18n._mfMessages.get('posts_likes_MF', {count: 3, ratio: 'med'})")
+ end
+ let!(:overriden_translation) do
+ Fabricate(
+ :translation_override,
+ translation_key: "admin_js.admin.user.penalty_history_MF",
+ value: "OVERRIDEN",
+ )
end
- it "finds locale for en_GB" do
- locale, filename =
- JsLocaleHelper.find_message_format_locale([:en_GB], fallback_to_english: false)
- expect(locale).to eq("en")
- expect(filename).to end_with("/en.js")
+ before { v8_ctx.eval(output) }
- locale, filename =
- JsLocaleHelper.find_message_format_locale(["en_GB"], fallback_to_english: false)
- expect(locale).to eq("en")
- expect(filename).to end_with("/en.js")
+ context "when locale is 'en'" do
+ let(:locale) { "en" }
+
+ it "generates messages for the 'en' locale only" do
+ expect(generated_locales).to eq %w[en]
+ end
+
+ it "translates messages properly" do
+ expect(
+ translated_message,
+ ).to eq "This topic has 3 replies with a very high like to post ratio\n"
+ end
+
+ context "when the translation is overriden" do
+ let(:translated_message) do
+ v8_ctx.eval(
+ "I18n._mfMessages.get('admin.user.penalty_history_MF', { SUSPENDED: 3, SILENCED: 2 })",
+ )
+ end
+
+ it "returns the overriden translation" do
+ expect(translated_message).to eq "OVERRIDEN"
+ end
+ end
end
- it "falls back to en when locale doesn't have own message format rules" do
- locale, filename =
- JsLocaleHelper.find_message_format_locale([:nonexistent], fallback_to_english: true)
- expect(locale).to eq("en")
- expect(filename).to end_with("/en.js")
+ context "when locale is not 'en'" do
+ let(:locale) { "fr" }
+
+ it "generates messages for the current locale and uses 'en' as fallback" do
+ expect(generated_locales).to match(%w[fr en])
+ end
+
+ it "translates messages properly" do
+ expect(
+ translated_message,
+ ).to eq "Ce sujet comprend 3 réponses avec un taux très élevé de « J'aime » par message\n"
+ end
+
+ context "when a translation is missing" do
+ before { v8_ctx.eval("delete I18n._mfMessages._data.fr.posts_likes_MF") }
+
+ it "returns the fallback translation" do
+ expect(
+ translated_message,
+ ).to eq "This topic has 3 replies with a very high like to post ratio\n"
+ end
+
+ context "when the fallback translation is overriden" do
+ let(:translated_message) do
+ v8_ctx.eval(
+ "I18n._mfMessages.get('admin.user.penalty_history_MF', { SUSPENDED: 3, SILENCED: 2 })",
+ )
+ end
+
+ before do
+ v8_ctx.eval("delete I18n._mfMessages._data.fr['admin.user.penalty_history_MF']")
+ end
+
+ it "returns the overriden fallback translation" do
+ expect(translated_message).to eq "OVERRIDEN"
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/plugin/instance_spec.rb b/spec/lib/plugin/instance_spec.rb
index 656b52aaac6..7bf68f1d8b9 100644
--- a/spec/lib/plugin/instance_spec.rb
+++ b/spec/lib/plugin/instance_spec.rb
@@ -523,9 +523,6 @@ TEXT
expect(DiscoursePluginRegistry.locales).to have_key(:foo_BAR)
expect(locale[:fallbackLocale]).to be_nil
- expect(locale[:message_format]).to eq(
- ["foo_BAR", "#{plugin_path}/lib/javascripts/locale/message_format/foo_BAR.js"],
- )
expect(locale[:moment_js]).to eq(
["foo_BAR", "#{plugin_path}/lib/javascripts/locale/moment_js/foo_BAR.js"],
)
@@ -536,9 +533,6 @@ TEXT
expect(Rails.configuration.assets.precompile).to include("locales/foo_BAR.js")
- expect(
- JsLocaleHelper.find_message_format_locale(["foo_BAR"], fallback_to_english: true),
- ).to eq(locale[:message_format])
expect(JsLocaleHelper.find_moment_locale(["foo_BAR"])).to eq(locale[:moment_js])
expect(JsLocaleHelper.find_moment_locale(["foo_BAR"], timezone_names: true)).to eq(
locale[:moment_js_timezones],
@@ -552,9 +546,6 @@ TEXT
expect(DiscoursePluginRegistry.locales).to have_key(:tup)
expect(locale[:fallbackLocale]).to eq("pt_BR")
- expect(locale[:message_format]).to eq(
- ["pt_BR", "#{Rails.root}/lib/javascripts/locale/pt_BR.js"],
- )
expect(locale[:moment_js]).to eq(
["pt-br", "#{Rails.root}/vendor/assets/javascripts/moment-locale/pt-br.js"],
)
@@ -565,9 +556,6 @@ TEXT
expect(Rails.configuration.assets.precompile).to include("locales/tup.js")
- expect(JsLocaleHelper.find_message_format_locale(["tup"], fallback_to_english: true)).to eq(
- locale[:message_format],
- )
expect(JsLocaleHelper.find_moment_locale(["tup"])).to eq(locale[:moment_js])
end
@@ -578,9 +566,6 @@ TEXT
expect(DiscoursePluginRegistry.locales).to have_key(:tlh)
expect(locale[:fallbackLocale]).to be_nil
- expect(locale[:message_format]).to eq(
- ["tlh", "#{plugin_path}/lib/javascripts/locale/message_format/tlh.js"],
- )
expect(locale[:moment_js]).to eq(
["tlh", "#{Rails.root}/vendor/assets/javascripts/moment-locale/tlh.js"],
)
@@ -588,9 +573,6 @@ TEXT
expect(Rails.configuration.assets.precompile).to include("locales/tlh.js")
- expect(JsLocaleHelper.find_message_format_locale(["tlh"], fallback_to_english: true)).to eq(
- locale[:message_format],
- )
expect(JsLocaleHelper.find_moment_locale(["tlh"])).to eq(locale[:moment_js])
end
@@ -602,7 +584,6 @@ TEXT
%w[
config/locales/client.foo_BAR.yml
config/locales/server.foo_BAR.yml
- lib/javascripts/locale/message_format/foo_BAR.js
lib/javascripts/locale/moment_js/foo_BAR.js
assets/locales/foo_BAR.js.erb
].each do |path|
diff --git a/spec/models/translation_override_spec.rb b/spec/models/translation_override_spec.rb
index d9080f89f79..4d5ff7d5365 100644
--- a/spec/models/translation_override_spec.rb
+++ b/spec/models/translation_override_spec.rb
@@ -170,20 +170,6 @@ RSpec.describe TranslationOverride do
expect(ovr.value).to eq("Click here alert('TEST');")
end
- it "stores js for a message format key" do
- I18n.backend.store_translations(:en, { some: { key_MF: "initial value" } })
- TranslationOverride.upsert!(
- "ru",
- "some.key_MF",
- "{NUM_RESULTS, plural, one {1 result} other {many} }",
- )
-
- ovr = TranslationOverride.where(locale: "ru", translation_key: "some.key_MF").first
- expect(ovr).to be_present
- expect(ovr.compiled_js).to start_with("function")
- expect(ovr.compiled_js).to_not match(/Invalid Format/i)
- end
-
describe "site cache" do
def cached_value(guardian, translation_key, locale:)
types_name, name_key, attribute = translation_key.split(".")
diff --git a/spec/requests/extra_locales_controller_spec.rb b/spec/requests/extra_locales_controller_spec.rb
index a9573510905..c240c293f74 100644
--- a/spec/requests/extra_locales_controller_spec.rb
+++ b/spec/requests/extra_locales_controller_spec.rb
@@ -46,6 +46,11 @@ RSpec.describe ExtraLocalesController do
expect(response.headers["Cache-Control"]).not_to include("max-age", "public", "immutable")
end
+ it "doesn’t generate the bundle twice" do
+ described_class.expects(:bundle_js).returns("JS").once
+ get "/extra-locales/admin", params: { v: "a" * 32 }
+ end
+
context "with plugin" do
before do
JsLocaleHelper.clear_cache!
@@ -120,7 +125,6 @@ RSpec.describe ExtraLocalesController do
ctx.eval("I18n = {};")
ctx.eval(response.body)
- expect(ctx.eval("typeof I18n._mfOverrides['js.client_MF']")).to eq("function")
expect(ctx.eval("I18n._overrides['#{I18n.locale}']['js.some_key']")).to eq(
"client-side translation",
)
@@ -175,15 +179,18 @@ RSpec.describe ExtraLocalesController do
expect(overrides["fr"]).to eq(
{ "js.some_key" => "some key (fr)", "js.only_fr" => "only French" },
)
-
- expect(ctx.eval("Object.keys(I18n._mfOverrides)")).to contain_exactly(
- "js.some_client_MF",
- "js.only_en_MF",
- "js.only_fr_MF",
- )
end
end
end
+
+ context "when requesting MessageFormat translations" do
+ before { JsLocaleHelper.stubs(:output_MF).with("en").returns("MF_TRANSLATIONS") }
+
+ it "returns the translations properly" do
+ get "/extra-locales/mf"
+ expect(response.body).to eq("MF_TRANSLATIONS")
+ end
+ end
end
describe ".bundle_js_hash" do
@@ -233,4 +240,14 @@ RSpec.describe ExtraLocalesController do
expect(ExtraLocalesController.client_overrides_exist?).to eq(true)
end
end
+
+ describe ".bundle_js_with_hash" do
+ before { described_class.stubs(:bundle_js).with("admin").returns("JS") }
+
+ it "returns both JS and its hash for a given bundle" do
+ expect(described_class.bundle_js_with_hash("admin")).to eq(
+ ["JS", Digest::MD5.hexdigest("JS")],
+ )
+ end
+ end
end
diff --git a/yarn.lock b/yarn.lock
index 1a0acd514e9..f93638972ba 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2204,6 +2204,42 @@
tslib "^2.4.1"
upath "^2.0.1"
+"@messageformat/core@^3.3.0":
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.3.0.tgz#31edd52a5f7d017adad85c929809f07741dcfd3f"
+ integrity sha512-YcXd3remTDdeMxAlbvW6oV9d/01/DZ8DHUFwSttO3LMzIZj3iO0NRw+u1xlsNNORFI+u0EQzD52ZX3+Udi0T3g==
+ dependencies:
+ "@messageformat/date-skeleton" "^1.0.0"
+ "@messageformat/number-skeleton" "^1.0.0"
+ "@messageformat/parser" "^5.1.0"
+ "@messageformat/runtime" "^3.0.1"
+ make-plural "^7.0.0"
+ safe-identifier "^0.4.1"
+
+"@messageformat/date-skeleton@^1.0.0":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz#980b8babe21a11433b6e1e8f6dc8c4cae4f5f56b"
+ integrity sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==
+
+"@messageformat/number-skeleton@^1.0.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz#e7c245c41a1b2722bc59dad68f4d454f761bc9b4"
+ integrity sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==
+
+"@messageformat/parser@^5.1.0":
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201"
+ integrity sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==
+ dependencies:
+ moo "^0.5.1"
+
+"@messageformat/runtime@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@messageformat/runtime/-/runtime-3.0.1.tgz#94d1f6c43265c28ef7aed98ecfcc0968c6c849ac"
+ integrity sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==
+ dependencies:
+ make-plural "^7.0.0"
+
"@mixer/parallel-prettier@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@mixer/parallel-prettier/-/parallel-prettier-2.0.3.tgz#970902afcd38c8c71155e1d089e5444896abc253"
@@ -3271,11 +3307,6 @@ async@^3.2.3:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
-async@~0.1.18:
- version "0.1.22"
- resolved "https://registry.yarnpkg.com/async/-/async-0.1.22.tgz#0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061"
- integrity sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==
-
async@~0.2.9:
version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
@@ -4467,11 +4498,6 @@ clone@^2.1.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
-coffee-script@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.2.0.tgz#b5e61e55f1ca8c4a9eb87d53aa0657ea43125b91"
- integrity sha512-vHxLlDOeI7/S+R/fr28ZjAhL3g+qcI+YbN0/S3N3yZa2aTh65XwFfbkeje+R3uSu1yQgXW2NvrzYJ7nznvRQaQ==
-
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -7412,15 +7438,6 @@ glob@^8.0.3, glob@^8.1.0:
minimatch "^5.0.1"
once "^1.3.0"
-glob@~3.1.9:
- version "3.1.21"
- resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd"
- integrity sha512-ANhy2V2+tFpRajE3wN4DhkNQ08KDr0Ir1qL12/cUe5+a7STEK8jkW4onUYuY8/06qAFuT5je7mjAqzx0eKI2tQ==
- dependencies:
- graceful-fs "~1.2.0"
- inherits "1"
- minimatch "~0.2.11"
-
global-modules@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
@@ -7544,11 +7561,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9,
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
-graceful-fs@~1.2.0:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364"
- integrity sha512-iiTUZ5vZ+2ZV+h71XAgwCSu6+NAizhFU3Yw8aC/hH5SQ3SnISqEqAek40imAFGtDcwJKNhXvSY+hzIolnLwcdQ==
-
graphemer@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
@@ -7926,11 +7938,6 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b"
- integrity sha512-Al67oatbRSo3RV5hRqIoln6Y5yMVbJSIn4jEJNL7VCImzq/kLr7vvb6sFRJXqr8rpHc/2kJOM+y0sPKN47VdzA==
-
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
@@ -9107,11 +9114,6 @@ lower-case@^2.0.2:
dependencies:
tslib "^2.0.3"
-lru-cache@2:
- version "2.7.3"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
- integrity sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==
-
lru-cache@^10.2.0:
version "10.2.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
@@ -9155,6 +9157,11 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
dependencies:
semver "^6.0.0"
+make-plural@^7.0.0, make-plural@^7.4.0:
+ version "7.4.0"
+ resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d"
+ integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg==
+
makeerror@1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
@@ -9329,18 +9336,6 @@ message-bus-client@^4.3.8:
resolved "https://registry.yarnpkg.com/message-bus-client/-/message-bus-client-4.3.8.tgz#5ee23c03236b250b13613034764a87881c350d4e"
integrity sha512-Vvrs0tOx5YcKeEoh7l1zATLVKt49FK34Vq/sloRbgDDQUB6VAbSVJPvH8RVxQ/PZGb9ScGzCMJCMnDyVa2p8CQ==
-messageformat@0.1.5:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-0.1.5.tgz#c7c561de181b04ef0fad36ca89c5cb942e5bb75c"
- integrity sha512-Ppf1WSwINnNdYUnbQnMaRj/3zY+QFJsGf/a2o98a62t3DX7TtUjn9O9CFDxiCkJPPtayf1W0YfvaTDYX6cBxBQ==
- dependencies:
- async "~0.1.18"
- coffee-script "~1.2.0"
- glob "~3.1.9"
- nopt "~2.0.0"
- underscore "~1.3.1"
- watchr "~1.0.0"
-
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -9454,14 +9449,6 @@ minimatch@^9.0.4:
dependencies:
brace-expansion "^2.0.1"
-minimatch@~0.2.11:
- version "0.2.14"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a"
- integrity sha512-zZ+Jy8lVWlvqqeM8iZB7w7KmQkoJn8djM585z88rywrEbzoqawVa9FR5p2hwD+y74nfuKOjmNvi9gtWJNLqHvA==
- dependencies:
- lru-cache "2"
- sigmund "~1.0.0"
-
minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
@@ -9527,6 +9514,11 @@ moment@2.30.1, moment@^2.29.4:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
+moo@^0.5.1:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c"
+ integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==
+
morgan@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
@@ -9721,13 +9713,6 @@ nopt@^3.0.6:
dependencies:
abbrev "1"
-nopt@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-2.0.0.tgz#ca7416f20a5e3f9c3b86180f96295fa3d0b52e0d"
- integrity sha512-uVTsuT8Hm3aN3VttY+BPKw4KU9lVpI0F22UAr/I1r6+kugMr3oyhMALkycikLcdfvGRsgzCYN48DYLBFcJEUVg==
- dependencies:
- abbrev "1"
-
normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
@@ -11022,6 +11007,11 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@^5.1.2,
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+safe-identifier@^0.4.1:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb"
+ integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==
+
safe-json-parse@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57"
@@ -11262,11 +11252,6 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-sigmund@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
- integrity sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==
-
signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
@@ -12298,11 +12283,6 @@ underscore@>=1.8.3, underscore@~1.13.2:
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441"
integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==
-underscore@~1.3.1:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.3.3.tgz#47ac53683daf832bfa952e1774417da47817ae42"
- integrity sha512-ddgUaY7xyrznJ0tbSUZgvNdv5qbiF6XcUBTrHgdCOVUrxJYWozD5KyiRjtIwds1reZ7O1iPLv5rIyqnVAcS6gg==
-
unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
@@ -12620,11 +12600,6 @@ watchpack@^2.4.1:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
-watchr@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/watchr/-/watchr-1.0.0.tgz#ce023fd59edae9430523031915c1812ff2302c27"
- integrity sha512-qaR72INd8EsMZ63VY+o91n6KHyy4gPUb0td2vsQQEWfgApg5NxXWBSh3zSXWoaJc7PS3imSNPggiHnQJfKxpNQ==
-
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"