fix: internationalize manifest.json (#2034)
* fix: internationalize manifest.json fixes #2020 * test: fix test
This commit is contained in:
parent
66fc202b5c
commit
85a5874876
|
@ -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
1
.gitignore
vendored
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
26
bin/getIntl.js
Normal 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, ' ')
|
||||||
|
}
|
|
@ -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": [
|
||||||
{
|
{
|
|
@ -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',
|
||||||
|
|
|
@ -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": "/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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') }))
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue