DEV: Output failing MF keys when compilation fails

Currently, when the MessageFormat compiler fails on some translations,
we just have the raw output from the compiler in the logs and that’s not
always very helpful.

Now, when there is an error, we iterate over the translation keys and
try to compile them one by one. When we detect one that is failing, it’s
added to a list that is now outputted in the logs. That way, it’s easier
to know which keys are not properly translated, and the problems can be
addressed quicker.

---

The previous implementation of this patch had a bug: it wasn’t handling
locales with country/region code properly. So instead of iterating over
the problematic keys, it was raising an error.
This commit is contained in:
Loïc Guitaut 2024-10-04 15:21:58 +02:00 committed by Loïc Guitaut
parent 745bc9ffa3
commit a0a7b439e1
2 changed files with 32 additions and 11 deletions

View File

@ -139,9 +139,9 @@ module JsLocaleHelper
message_formats =
I18n.fallbacks[locale]
.each_with_object({}) do |l, hash|
.each_with_object(HashWithIndifferentAccess.new) do |l, hash|
translations = translations_for(l, no_fallback: true)
hash[l.to_s.dasherize] = remove_message_formats!(translations, l).merge(
hash[l] = remove_message_formats!(translations, l).merge(
TranslationOverride
.mf_locales(l)
.pluck(:translation_key, :value)
@ -150,7 +150,8 @@ module JsLocaleHelper
)
end
.compact_blank
compiled = MessageFormat.compile(message_formats.keys, message_formats, strict: false)
js_message_formats = message_formats.transform_keys(&:dasherize)
compiled = MessageFormat.compile(js_message_formats.keys, js_message_formats, strict: false)
transpiled = DiscourseJsProcessor.transpile(<<~JS, "", "discourse-mf")
import Messages from '@messageformat/runtime/messages';
#{compiled.sub("export default", "const msgData =")};
@ -163,7 +164,18 @@ module JsLocaleHelper
require("discourse-mf");
JS
rescue => e
Rails.logger.error("Failed to compile message formats for #{locale} '#{e}'")
js_locale = locale.to_s.dasherize
message_formats[locale]
.filter_map do |key, value|
next if MessageFormat.compile(js_locale, value, strict: false)
rescue StandardError
key
end
.then do |strings|
Rails.logger.error(
"Failed to compile message formats for #{locale}.\n\nBroken strings are: #{strings.join(", ")}\n\nError: #{e}",
)
end
<<~JS
console.error("Failed to compile message formats for #{locale}. Some translation strings will be missing.");
JS

View File

@ -161,25 +161,29 @@ RSpec.describe JsLocaleHelper do
fab!(:overriden_translation_ja) do
Fabricate(:translation_override, locale: "ja", translation_key: "js.posts_likes_MF")
end
fab!(:overriden_translation_he) do
Fabricate(:translation_override, locale: "he", translation_key: "js.posts_likes_MF")
fab!(:overriden_translation_zh_tw) do
Fabricate(:translation_override, locale: "zh_TW", translation_key: "js.posts_likes_MF")
end
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(:fake_logger) { FakeLogger.new }
before do
Rails.logger.broadcast_to(fake_logger)
overriden_translation_ja.update_columns(
value: "{ count, plural, one {返信 # 件、} other {返信 # 件、} }",
)
overriden_translation_he.update_columns(value: "{ count, plural, ")
overriden_translation_zh_tw.update_columns(value: "{ count, plural, ")
v8_ctx.eval(output)
end
after { Rails.logger.stop_broadcasting_to(fake_logger) }
context "when locale is 'en'" do
let(:locale) { "en" }
let(:locale) { :en }
it "generates messages for the 'en' locale only" do
expect(generated_locales).to eq %w[en]
@ -205,7 +209,7 @@ RSpec.describe JsLocaleHelper do
end
context "when locale is not 'en'" do
let(:locale) { "fr" }
let(:locale) { :fr }
it "generates messages for the current locale and uses 'en' as fallback" do
expect(generated_locales).to match(%w[fr en])
@ -245,7 +249,7 @@ RSpec.describe JsLocaleHelper do
end
context "when locale contains invalid plural keys" do
let(:locale) { "ja" }
let(:locale) { :ja }
it "does not raise an error" do
expect(generated_locales).to match(%w[ja en])
@ -253,11 +257,16 @@ RSpec.describe JsLocaleHelper do
end
context "when locale contains malformed messages" do
let(:locale) { "he" }
let(:locale) { :zh_TW }
it "raises an error" do
expect(output).to match(/Failed to compile message formats/)
end
it "logs which keys are problematic" do
output
expect(fake_logger.errors).to include(/posts_likes_MF/)
end
end
end