mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 17:57:24 +08:00
7ef482a292
* FIX: Use pluralized string * REFACTOR: Fix misuse of pluralized string * REFACTOR: Fix misuse of pluralized string * DEV: Remove linting of `one` key in MessageFormat string, it doesn't work * REFACTOR: Fix misuse of pluralized string This also ensures that the URL works on subfolder and shows the site setting link only for admins instead of staff. The string is quite complicated, so the best option was to switch to MessageFormat. * REFACTOR: Fix misuse of pluralized string * FIX: Use pluralized string This also ensures that the URL works on subfolder and shows the site setting link only for admins instead of staff. * REFACTOR: Correctly pluralize reaction tooltips in chat This also ensures that maximum 5 usernames are shown and fixes the number of "others" which was off by 1 if the current user reacted on a message. * REFACTOR: Use translatable string as comma separator * DEV: Add comment to translation to clarify the meaning of `%{identifier}` * REFACTOR: Use translatable comma separator and use explicit interpolation keys * REFACTOR: Don't interpolate lowercase channel status * REFACTOR: Fix misuse of pluralized string * REFACTOR: Don't interpolate channel status * REFACTOR: Use %{count} interpolation key * REFACTOR: Fix misuse of pluralized string * REFACTOR: Correctly pluralize DM chat channel titles
131 lines
3.5 KiB
Ruby
Executable File
131 lines
3.5 KiB
Ruby
Executable File
# frozen_string_literal: true
|
|
|
|
require "colored2"
|
|
require "psych"
|
|
|
|
class I18nLinter
|
|
def initialize(filenames_or_patterns)
|
|
@filenames = filenames_or_patterns.map { |fp| Dir[fp] }.flatten
|
|
@errors = {}
|
|
end
|
|
|
|
def run
|
|
has_errors = false
|
|
|
|
@filenames.each do |filename|
|
|
validator = LocaleFileValidator.new(filename)
|
|
|
|
if validator.has_errors?
|
|
validator.print_errors
|
|
has_errors = true
|
|
end
|
|
end
|
|
|
|
exit 1 if has_errors
|
|
end
|
|
end
|
|
|
|
class LocaleFileValidator
|
|
ERROR_MESSAGES = {
|
|
invalid_relative_links:
|
|
"The following keys have relative links, but do not start with %{base_url} or %{base_path}:",
|
|
invalid_relative_image_sources:
|
|
"The following keys have relative image sources, but do not start with %{base_url} or %{base_path}:",
|
|
invalid_interpolation_key_format:
|
|
"The following keys use {{key}} instead of %{key} for interpolation keys:",
|
|
wrong_pluralization_keys:
|
|
"Pluralized strings must have only the sub-keys 'one' and 'other'.\nThe following keys have missing or additional keys:",
|
|
invalid_one_keys:
|
|
"The following keys contain the number 1 instead of the interpolation key %{count}:",
|
|
}
|
|
|
|
PLURALIZATION_KEYS = %w[zero one two few many other]
|
|
ENGLISH_KEYS = %w[one other]
|
|
|
|
def initialize(filename)
|
|
@filename = filename
|
|
@errors = {}
|
|
end
|
|
|
|
def has_errors?
|
|
yaml = Psych.safe_load(File.read(@filename), aliases: true)
|
|
yaml = yaml[yaml.keys.first]
|
|
|
|
validate_pluralizations(yaml)
|
|
validate_content(yaml)
|
|
|
|
@errors.any? { |_, value| value.any? }
|
|
end
|
|
|
|
def print_errors
|
|
puts "", "Errors in #{@filename}".red
|
|
|
|
@errors.each do |type, keys|
|
|
next if keys.empty?
|
|
|
|
ERROR_MESSAGES[type].split("\n").each { |msg| puts " #{msg}" }
|
|
keys.each { |key| puts " * #{key}" }
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def each_translation(hash, parent_key = "", &block)
|
|
hash.each do |key, value|
|
|
current_key = parent_key.empty? ? key : "#{parent_key}.#{key}"
|
|
|
|
if Hash === value
|
|
each_translation(value, current_key, &block)
|
|
else
|
|
yield(current_key, value.to_s)
|
|
end
|
|
end
|
|
end
|
|
|
|
def validate_content(yaml)
|
|
@errors[:invalid_relative_links] = []
|
|
@errors[:invalid_relative_image_sources] = []
|
|
@errors[:invalid_interpolation_key_format] = []
|
|
|
|
each_translation(yaml) do |key, value|
|
|
@errors[:invalid_relative_links] << key if value.match?(%r{href\s*=\s*["']/[^/]|\]\(/[^/]}i)
|
|
|
|
@errors[:invalid_relative_image_sources] << key if value.match?(%r{src\s*=\s*["']/[^/]}i)
|
|
|
|
if value.match?(/{{.+?}}/) && !key.end_with?("_MF")
|
|
@errors[:invalid_interpolation_key_format] << key
|
|
end
|
|
end
|
|
end
|
|
|
|
def each_pluralization(hash, parent_key = "", &block)
|
|
hash.each do |key, value|
|
|
if Hash === value
|
|
current_key = parent_key.empty? ? key : "#{parent_key}.#{key}"
|
|
each_pluralization(value, current_key, &block)
|
|
elsif PLURALIZATION_KEYS.include? key
|
|
yield(parent_key, hash)
|
|
end
|
|
end
|
|
end
|
|
|
|
def validate_pluralizations(yaml)
|
|
@errors[:wrong_pluralization_keys] = []
|
|
@errors[:invalid_one_keys] = []
|
|
|
|
each_pluralization(yaml) do |key, hash|
|
|
# ignore errors from some ActiveRecord messages
|
|
next if key.include?("messages.restrict_dependent_destroy")
|
|
|
|
@errors[:wrong_pluralization_keys] << key if hash.keys.sort != ENGLISH_KEYS
|
|
|
|
one_value = hash["one"]
|
|
if one_value && one_value.include?("1") && !one_value.match?(/%{count}|{{count}}/)
|
|
@errors[:invalid_one_keys] << key
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
I18nLinter.new(ARGV).run
|