fix: internationalize manifest.json (#2034)

* fix: internationalize manifest.json

fixes #2020

* test: fix test
This commit is contained in:
Nolan Lawson 2021-04-11 19:40:24 -07:00 committed by GitHub
parent 66fc202b5c
commit 85a5874876
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 61 additions and 74 deletions

View file

@ -14,6 +14,7 @@ tests
/static/icons.svg /static/icons.svg
/static/inline-script.js.map /static/inline-script.js.map
/static/emoji-*.json /static/emoji-*.json
/static/manifest.json
/src/inline-script/checksum.js /src/inline-script/checksum.js
yarn-error.log yarn-error.log
/vercel.json /vercel.json

1
.gitignore vendored
View file

@ -8,6 +8,7 @@
/static/icons.svg /static/icons.svg
/static/inline-script.js.map /static/inline-script.js.map
/static/emoji-*.json /static/emoji-*.json
/static/manifest.json
/src/inline-script/checksum.js /src/inline-script/checksum.js
yarn-error.log yarn-error.log

View file

@ -8,5 +8,6 @@
/static/icons.svg /static/icons.svg
/static/inline-script.js.map /static/inline-script.js.map
/static/emoji-*.json /static/emoji-*.json
/static/manifest.json
/src/inline-script/checksum.js /src/inline-script/checksum.js
yarn-error.log yarn-error.log

View file

@ -2,6 +2,7 @@ import path from 'path'
import fs from 'fs' import fs from 'fs'
import { promisify } from 'util' import { promisify } from 'util'
import { LOCALE } from '../src/routes/_static/intl' import { LOCALE } from '../src/routes/_static/intl'
import { getIntl, trimWhitespace } from './getIntl'
const readFile = promisify(fs.readFile) const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile) const writeFile = promisify(fs.writeFile)
@ -34,7 +35,7 @@ async function getFirstExistingEmojiI18nFile () {
} }
} }
async function main () { async function buildEmojiI18nFile () {
const json = await getFirstExistingEmojiI18nFile() const json = await getFirstExistingEmojiI18nFile()
if (!json) { if (!json) {
@ -48,6 +49,26 @@ async function main () {
) )
} }
async function buildManifestJson () {
const template = await readFile(path.resolve(__dirname, '../src/build/manifest.json'), 'utf8')
// replace {@intl.foo}
const output = template
.replace(/{intl\.([^}]+)}/g, (match, p1) => trimWhitespace(getIntl(p1)))
await writeFile(
path.resolve(__dirname, '../static/manifest.json'),
JSON.stringify(JSON.parse(output)), // minify json
'utf8'
)
}
async function main () {
await Promise.all([
buildEmojiI18nFile(),
buildManifestJson()
])
}
main().catch(err => { main().catch(err => {
console.error(err) console.error(err)
process.exit(1) process.exit(1)

26
bin/getIntl.js Normal file
View file

@ -0,0 +1,26 @@
import { get } from 'lodash-es'
import { DEFAULT_LOCALE, LOCALE } from '../src/routes/_static/intl'
import path from 'path'
const intl = require(path.join(__dirname, '../src/intl', LOCALE + '.js')).default
const defaultIntl = require(path.join(__dirname, '../src/intl', DEFAULT_LOCALE + '.js')).default
export function warningOrError (message) { // avoid crashing the whole server on `yarn dev`
if (process.env.NODE_ENV === 'production') {
throw new Error(message)
}
console.warn(message)
return '(Placeholder intl string)'
}
export function getIntl (path) {
const res = get(intl, path, get(defaultIntl, path))
if (typeof res !== 'string') {
return warningOrError('Unknown intl string: ' + JSON.stringify(path))
}
return res
}
export function trimWhitespace (str) {
return str.trim().replace(/\s+/g, ' ')
}

View file

@ -1,9 +1,9 @@
{ {
"background_color": "#ffffff", "background_color": "#ffffff",
"theme_color": "#4169e1", "theme_color": "#4169e1",
"name": "Pinafore for Mastodon", "name": "{intl.longAppName}",
"short_name": "Pinafore", "short_name": "{intl.appName}",
"description": "Alternative web client for Mastodon, focused on speed and simplicity.", "description": "{intl.appDescription}",
"categories": [ "categories": [
"social" "social"
], ],
@ -114,9 +114,7 @@
], ],
"shortcuts": [ "shortcuts": [
{ {
"name": "Write a toot", "name": "{intl.newStatus}",
"short_name": "New toot",
"description": "Start composing a new toot",
"url": "/?pwa=true&compose=true", "url": "/?pwa=true&compose=true",
"icons": [ "icons": [
{ {
@ -126,9 +124,7 @@
] ]
}, },
{ {
"name": "View notifications", "name": "{intl.notifications}",
"short_name": "Notifications",
"description": "View your new notifications",
"url": "/notifications?pwa=true", "url": "/notifications?pwa=true",
"icons": [ "icons": [
{ {

View file

@ -27,6 +27,9 @@ export default {
Here is the <a href="/settings/about#privacy-policy" rel="prefetch">privacy policy</a>. Here is the <a href="/settings/about#privacy-policy" rel="prefetch">privacy policy</a>.
</p> </p>
`, `,
// Manifest
longAppName: 'Pinafore for Mastodon',
newStatus: 'New toot',
// Generic UI // Generic UI
loading: 'Loading', loading: 'Loading',
okay: 'OK', okay: 'OK',

View file

@ -1,37 +0,0 @@
{
"version": "1.0.0",
"name": "Pinafore",
"description": "Mastodon client",
"type": "web",
"launch_path": "/",
"icons": {
"48": "/icon-48.png",
"72": "/icon-72.png",
"96": "/icon-96.png"
},
"developer": {
"name": "Nolan Lawson",
"url": "https://nolanlawson.com"
},
"locales": {
"en-US": {
"name": "Pinafore",
"subtitle": "Mastodon client",
"description": "Mastodon client, focused on speed and simplicity."
}
},
"default_locale": "en-US",
"permissions": {
"serviceworker": {
"description": "Needed for offline storage and notifications."
},
"desktop-notification": {
"description": "Needed for notifications."
}
},
"messages": [
{
"serviceworker-notification": "/"
}
]
}

View file

@ -16,7 +16,7 @@ describe('test-intl.js', () => {
it('has no unused intl strings', async () => { it('has no unused intl strings', async () => {
const keys = Object.keys(enIntl) const keys = Object.keys(enIntl)
const allSourceFilenames = (await globby([path.join(__dirname, '../../src/**/*.{js,html}')])) const allSourceFilenames = (await globby([path.join(__dirname, '../../src/**/*.{js,html,json}')]))
.filter(file => !file.includes('/intl/')) .filter(file => !file.includes('/intl/'))
const allSourceFiles = await Promise.all( const allSourceFiles = await Promise.all(
allSourceFilenames.map(async name => ({ name, content: await readFile(name, 'utf8') })) allSourceFilenames.map(async name => ({ name, content: await readFile(name, 'utf8') }))

View file

@ -1,32 +1,7 @@
// Inject intl statements into a Svelte v2 HTML file as well as some JS files like timeago.js // Inject intl statements into a Svelte v2 HTML file as well as some JS files like timeago.js
// We do this for perf reasons, to make the output smaller and avoid needing to have a huge JSON file of translations // We do this for perf reasons, to make the output smaller and avoid needing to have a huge JSON file of translations
import { get } from 'lodash-es'
import parse from 'format-message-parse' import parse from 'format-message-parse'
import { DEFAULT_LOCALE, LOCALE } from '../src/routes/_static/intl' import { getIntl, warningOrError, trimWhitespace } from '../bin/getIntl'
import path from 'path'
const intl = require(path.join(__dirname, '../src/intl', LOCALE + '.js')).default
const defaultIntl = require(path.join(__dirname, '../src/intl', DEFAULT_LOCALE + '.js')).default
function warningOrError (message) { // avoid crashing the whole server on `yarn dev`
if (process.env.NODE_ENV === 'production') {
throw new Error(message)
}
console.warn(message)
return '(Placeholder intl string)'
}
function getIntl (path) {
const res = get(intl, path, get(defaultIntl, path))
if (typeof res !== 'string') {
return warningOrError('Unknown intl string: ' + JSON.stringify(path))
}
return res
}
function trimWhitespace (str) {
return str.trim().replace(/\s+/g, ' ')
}
export default function (source) { export default function (source) {
const res = source const res = source