Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ARTIFACT_DESTINATION_FILE ?= ./tmp/idp.tar.gz
lint_analytics_events_sorted \
lint_country_dialing_codes \
lint_erb \
lint_font_glyphs \
lint_lockfiles \
lint_new_typescript_files \
lint_optimized_assets \
Expand Down Expand Up @@ -87,6 +88,8 @@ endif
# Other
@echo "--- lint yaml ---"
make lint_yaml
@echo "--- lint font glyphs ---"
make lint_font_glyphs
@echo "--- lint Yarn workspaces ---"
make lint_yarn_workspaces
@echo "--- lint new TypeScript files ---"
Expand All @@ -110,6 +113,15 @@ lint_erb: ## Lints ERB files
lint_yaml: normalize_yaml ## Lints YAML files
(! git diff --name-only | grep "^config/.*\.yml$$") || (echo "Error: Run 'make normalize_yaml' to normalize YAML"; exit 1)

lint_font_glyphs: ## Lints to validate content glyphs match expectations from fonts
scripts/yaml_characters \
--exclude-locale=zh \
--exclude-gem-path=faker \
--exclude-gem-path=good_job \
--exclude-gem-path=i18n-tasks \
> app/assets/fonts/glyphs.txt
(! git diff --name-only | grep "glyphs\.txt$$") || (echo "Error: New character data found. Follow 'Fonts' instructions in 'docs/frontend.md' to regenerate fonts."; exit 1)

lint_yarn_workspaces: ## Lints Yarn workspace packages
scripts/validate-workspaces.mjs

Expand Down
1 change: 1 addition & 0 deletions app/assets/fonts/glyphs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!"#$%&'(),-./0123456789:;>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ «»¿ÀÁÈÉÊÎÓÚàáâãçèéêëíîïñóôùúû ‑—‘’“”…‹中体文简
Binary file removed app/assets/fonts/public-sans/PublicSans-Black.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Black.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-BlackItalic.woff2
Binary file not shown.
Binary file removed app/assets/fonts/public-sans/PublicSans-Bold.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Bold.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-BoldItalic.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-ExtraBold.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-ExtraBoldItalic.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-ExtraLight.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-ExtraLightItalic.woff2
Binary file not shown.
Binary file removed app/assets/fonts/public-sans/PublicSans-Italic.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Italic.woff2
Binary file not shown.
Binary file removed app/assets/fonts/public-sans/PublicSans-Light.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Light.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-LightItalic.woff2
Binary file not shown.
Binary file removed app/assets/fonts/public-sans/PublicSans-Medium.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Medium.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-MediumItalic.woff2
Binary file not shown.
Binary file removed app/assets/fonts/public-sans/PublicSans-Regular.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Regular.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-SemiBold.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-SemiBoldItalic.woff2
Binary file not shown.
Binary file removed app/assets/fonts/public-sans/PublicSans-Thin.woff
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-Thin.woff2
Binary file not shown.
Binary file not shown.
Binary file modified app/assets/fonts/public-sans/PublicSans-ThinItalic.woff2
Binary file not shown.
18 changes: 18 additions & 0 deletions docs/frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,24 @@ Debugging these stack traces can be difficult, since files in production are min

The output of the `sourcemap-lookup` command should include "Original Position" and "Code Section" of the code which triggered the error.

## Fonts

Font files are optimized to remove unused character data. If a new character is added to content, the font files must be regenerated:

1. [Download Public Sans](https://public-sans.digital.gov/) and extract it to your project's `tmp/` directory
2. Install [glyphhanger](https://github.com/zachleat/glyphhanger) and its dependencies:
1. `npm install -g glyphhanger`
2. `pip install fonttools brotli`
3. Scrape content for character data:
1. `make lint_font_glyphs`
4. Subset the original Public Sans fonts to include only used character data:
1. `glyphhanger app/assets/fonts/glyphs.txt --formats=woff2 --subset="tmp/public-sans-v2/fonts/ttf/PublicSans-*.ttf"`
5. Replace font files with new subset fonts:
1. `cd tmp/public-sans-v2/fonts/ttf`
2. `find . -name "*-subset.woff2" -exec sh -c 'cp $1 "../../../../app/assets/fonts/public-sans/${1%-subset.woff2}.woff2"' _ {} \;`

At this point, your working directory should reflect changes to all of the files within `app/assets/fonts/public-sans`.

## Devices

The application should support:
Expand Down
64 changes: 64 additions & 0 deletions scripts/yaml_characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

Comment thread
aduth marked this conversation as resolved.
require 'pathname'
require_relative '../config/environment'

excluded_locales = []
excluded_gem_paths = []
OptionParser.new do |opts|
opts.banner = <<~TXT
Usage
=======================

#{$PROGRAM_NAME} [OPTIONS]

Prints all unique characters from Rails loaded I18n data, which can be used in combination
with font subsetting to optimize font sizes.

TXT

opts.on('--exclude-locale=LOCALE', 'Disregard characters from the given locale') do |locale|
excluded_locales << locale.to_sym
end

opts.on('--exclude-gem-path=GEM', 'Disregard characters loaded by the given gem') do |gem|
excluded_gem_paths << Gem.loaded_specs[gem].full_gem_path
end

opts.on('-h', '--help', 'Prints this help') do
STDOUT.puts opts
exit
end
end.parse!(ARGV)

def sanitize(string)
string.gsub(/<.+?>/, '').gsub(/%{.+?}/, '').gsub(/[\n\r\t]/, '')
end

def hash_values(hash)
hash.values.flat_map do |value|
case value
when Hash
hash_values(value)
when Array
value
else
[value]
end
end
end

I18n.load_path.reject! do |load_path|
excluded_gem_paths.any? { |gem_path| load_path.start_with?(gem_path) }
end

I18n.backend.eager_load!

data = I18n.backend.translations.slice(*I18n.available_locales - excluded_locales)
Comment on lines +56 to +58
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should do something similar to this in spec/i18n_spec.rb. Interestingly i18n-tasks doesn't load all string data eagerly like this, so we miss some strings in those specs. Having a more complete set of strings to test against could help prevent issues like one we encountered a few weeks back.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense to me to add that there

strings = hash_values(data)
joined_string = strings.join('')
sanitized_string = sanitize(joined_string)
characters = sanitized_string.chars.to_a.uniq.sort.join('')

STDOUT.puts characters