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
19 changes: 19 additions & 0 deletions format-explorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @intlify/message-format-explorer

intlify message format explorer

## :cd: Installation

```sh
$ npm install
```

## :rocket: Run the format explorer

```sh
$ npm run dev
```

## :copyright: License

[MIT](http://opensource.org/licenses/MIT)
33 changes: 33 additions & 0 deletions format-explorer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@intlify/message-format-explorer",
"description": "intlify message format explorer",
"version": "0.0.0",
"private": true,
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server",
"clean": "rm -rf ./dist"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.0.0",
"css-loader": "^4.3.0",
"file-loader": "^6.1.0",
"ts-loader": "^7.0.5",
"url-loader": "^4.1.0",
"typescript": "^4.0.3",
"vue-loader": "^16.0.0-beta.8",
"webpack": "^4.44.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"html-webpack-plugin": "^4.3.0",
"monaco-editor-webpack-plugin": "^2.0.0",
"style-loader": "^1.2.1"
},
"dependencies": {
"vue": "^3.0.0",
"vue-i18n": "link:..",
"monaco-editor": "^0.21.2"
}
}
141 changes: 141 additions & 0 deletions format-explorer/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Navigation from './components/Navigation.vue'
import Editor from './components/Editor.vue'
import { baseCompile } from 'vue-i18n'
import type { CompileError, CompileOptions } from 'vue-i18n'

interface PersistedState {
src?: string
options: CompileOptions
}

export default defineComponent({
name: 'App',

components: {
Navigation,
Editor
},

setup() {
/**
* states
*/
const genCodes = ref<string>('')
const compileErrors = ref<CompileError[]>([])
const persistedState: PersistedState = JSON.parse(
decodeURIComponent(window.location.hash.slice(1)) ||
localStorage.getItem('state') ||
`{}`
)
const initialCodes = persistedState.src || 'hello {name}!'

/**
* utilties
*/
let lastSuccessCode: string
function compile(message: string): string {
console.clear()

try {
const start = performance.now()

const errors: CompileError[] = []
const { code, ast } = baseCompile(message, {
onError: err => errors.push(err)
})
if (errors.length > 0) {
console.error(errors)
}

console.log(`Compiled in ${(performance.now() - start).toFixed(2)}ms.`)
compileErrors.value = errors
console.log(`AST: `, ast)

const evalCode = new Function(`return ${code}`)()
lastSuccessCode =
evalCode.toString() + `\n\n// Check the console for the AST`
} catch (e) {
lastSuccessCode = `/* ERROR: ${e.message} (see console for more info) */`
console.error(e)
}

return lastSuccessCode
}

/**
* envet handlers
*/
const onChange = (message: string): void => {
const state = JSON.stringify({ src: message } as PersistedState)
localStorage.setItem('state', state)
window.location.hash = encodeURIComponent(state)
genCodes.value = compile(message)
}

// setup context
return {
initialCodes,
genCodes,
compileErrors,
onChange
}
}
})
</script>

<template>
<div class="container">
<div class="navigation">
<Navigation class="navigation" />
</div>
<div class="operation">
<Editor
class="input"
language="intlify"
:code="initialCodes"
:errors="compileErrors"
:debounce="true"
@change="onChange"
/>
<!-- prettier-ignore -->
<Editor
class="output"
:code="genCodes"
/>
</div>
</div>
</template>

<style scoped>
.container {
width: 100%;
height: 100%;
}
.navigation {
width: 100%;
height: 5%;
box-sizing: border-box;
background-color: var(--bg);
border-bottom: 1px solid var(--border);
}
.operation {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
}
.input {
margin-top: 0.6rem;
flex: 2;
width: 30%;
height: 100%;
}
.output {
margin-top: 0.6rem;
flex: 3;
width: 70%;
height: 100%;
}
</style>
115 changes: 115 additions & 0 deletions format-explorer/src/components/Editor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<script lang="ts">
import { defineComponent, ref, onMounted, watchEffect } from 'vue'
import theme from '../theme'
import * as monaco from 'monaco-editor'
import type { PropType } from 'vue'
import type { CompileError } from 'vue-i18n'

/* eslint-disable */
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number = 300
): T {
let prevTimer: number | null = null
return ((...args: any[]) => {
if (prevTimer) {
clearTimeout(prevTimer)
}
prevTimer = window.setTimeout(() => {
fn(...args)
prevTimer = null
}, delay)
}) as any
}
/* eslint-enable */

export type Foo = number

export default defineComponent({
name: 'Editor',

props: {
code: {
type: String,
default: () => ''
},
debounce: {
type: Boolean,
default: () => false
},
language: {
type: String,
default: () => 'javascript'
},
errors: {
type: Array as PropType<CompileError[]>,
default: () => []
}
},

emits: {
change: null
},

setup(props, { emit }) {
const container = ref<HTMLElement | null>(null)

function formatError(err: CompileError) {
const loc = err.location!
return {
severity: monaco.MarkerSeverity.Error,
startLineNumber: loc.start.line,
startColumn: loc.start.column,
endLineNumber: loc.end.line,
endColumn: loc.end.column,
message: `intlify message compilation error: ${err.message}`,
code: String(err.code)
}
}

onMounted(() => {
if (container.value == null) {
return
}

monaco.editor.defineTheme('my-theme', theme)
monaco.editor.setTheme('my-theme')

const editor = monaco.editor.create(container.value, {
value: [props.code].join('\n'),
wordWrap: 'bounded',
language: props.language,
fontSize: 14,
scrollBeyondLastLine: false,
renderWhitespace: 'selection',
tabSize: 2,
minimap: {
enabled: false
}
})
window.addEventListener('resize', () => editor.layout())

const changeEmitter = props.debounce
? debounce(() => emit('change', editor.getValue()))
: () => emit('change', editor.getValue())

editor.onDidChangeModelContent(changeEmitter)

watchEffect(() => editor.setValue(props.code!))
watchEffect(() => {
monaco.editor.setModelMarkers(
editor.getModel()!,
`vue-i18n`,
props.errors.filter(e => e.location).map(formatError)
)
})
})

return { container }
}
})
</script>

<template>
<div ref="container" />
</template>
12 changes: 12 additions & 0 deletions format-explorer/src/components/Navigation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<div class="navigation-root">
<h2>Intlify Message Format Explorer</h2>
</div>
</template>

<style scoped>
h2 {
margin: 0;
padding: 0.4rem 0 0.4rem 2rem;
}
</style>
21 changes: 21 additions & 0 deletions format-explorer/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
body {
--bg: #1D1F21;
--fg: #fff;
--border: #333;
--in-bg: white;
--in-fg: black;
--in-border: #666;
margin: 0;
background-color: var(--bg);
}

#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: var(--fg);
margin: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
}
11 changes: 11 additions & 0 deletions format-explorer/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>intlify message format explorer</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
5 changes: 5 additions & 0 deletions format-explorer/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')
5 changes: 5 additions & 0 deletions format-explorer/src/shim-vue.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module '*.vue' {
import { defineComponent } from 'vue'
const component: ReturnType<typeof defineComponent>
export default component
}
Loading