Add translations support (#116)
This commit is contained in:
parent
1c18563dfb
commit
591ce0894e
8
crowdin.yml
Normal file
8
crowdin.yml
Normal file
@ -0,0 +1,8 @@
|
||||
project_id: 518556
|
||||
preserve_hierarchy: true
|
||||
commit_message: '[ci skip]'
|
||||
|
||||
files:
|
||||
- source: /locales/en-US/*
|
||||
dest: /%original_file_name%
|
||||
translation: /locales/%locale%/%original_file_name%
|
||||
@ -1,7 +1,11 @@
|
||||
import { resolve } from 'path'
|
||||
import { resolve, basename } from 'path'
|
||||
import svgLoader from 'vite-svg-loader'
|
||||
import eslintPlugin from 'vite-plugin-eslint'
|
||||
import { icuMessages } from '@vintl/unplugin/vite'
|
||||
import virtual from '@rollup/plugin-virtual'
|
||||
import { globSync } from 'glob'
|
||||
|
||||
/** @type {import('vitepress').SiteConfig} */
|
||||
export default {
|
||||
title: 'Omorphia',
|
||||
description: 'A components library used for Modrinth.',
|
||||
@ -80,11 +84,40 @@ export default {
|
||||
},
|
||||
}),
|
||||
eslintPlugin(),
|
||||
icuMessages({
|
||||
filter: (id) => id.endsWith('.json?messages'),
|
||||
pluginsWrapping: true,
|
||||
}),
|
||||
virtual({
|
||||
'@modrinth/omorphia-dev/locales/index.js': (() => {
|
||||
const localeDirs = globSync('../../locales/*', { cwd: __dirname, absolute: true })
|
||||
let output = 'export const localeDefinitions = Object.create(null);\n'
|
||||
for (const localeDir of localeDirs) {
|
||||
const tag = basename(localeDir)
|
||||
output += `localeDefinitions[${JSON.stringify(tag)}] = {\n`
|
||||
output += '\tasync importFunction() {\n'
|
||||
output += `\t\tconst messages = Object.create(null);\n`
|
||||
for (const filePath of globSync('*', { cwd: localeDir, absolute: true })) {
|
||||
const fileName = basename(filePath)
|
||||
if (fileName === 'index.json') {
|
||||
output += `\t\tObject.assign(messages, await import(${JSON.stringify(
|
||||
`${filePath}?messages`
|
||||
)}).then((mod) => mod['default']));\n`
|
||||
}
|
||||
}
|
||||
output += '\t\treturn { messages }\n'
|
||||
output += '\t},\n'
|
||||
output += '}\n'
|
||||
}
|
||||
return output
|
||||
})(),
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, '../../lib'),
|
||||
omorphia: resolve(__dirname, '../../lib'),
|
||||
'@formatjs/icu-messageformat-parser': '@formatjs/icu-messageformat-parser/lib/no-parser',
|
||||
},
|
||||
dedupe: ['vue'],
|
||||
},
|
||||
|
||||
15
docs/.vitepress/env.d.ts
vendored
Normal file
15
docs/.vitepress/env.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '@modrinth/omorphia-dev/locales/index.js' {
|
||||
interface LocaleExport {
|
||||
messages: Record<string, any[]>
|
||||
}
|
||||
|
||||
interface LocaleDefinition {
|
||||
importFunction(): Promise<LocaleExport>
|
||||
}
|
||||
|
||||
const localeDefinitions: Partial<Record<string, LocaleDefinition>>
|
||||
|
||||
export { localeDefinitions }
|
||||
}
|
||||
73
docs/.vitepress/theme/LanguageSwitcher.vue
Normal file
73
docs/.vitepress/theme/LanguageSwitcher.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import { useVIntl } from '@vintl/vintl'
|
||||
import { DropdownSelect } from 'omorphia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const { $locales, $config, changeLocale } = useVIntl()
|
||||
|
||||
const getLocaleDisplayName = (() => {
|
||||
const cache = new Map<string, Intl.DisplayNames>()
|
||||
|
||||
return function getLocaleDisplayName(locale: string) {
|
||||
let displayNames = cache.get(locale)
|
||||
if (displayNames == null) {
|
||||
displayNames = new Intl.DisplayNames(locale, {
|
||||
type: 'language',
|
||||
languageDisplay: 'standard',
|
||||
})
|
||||
cache.set(locale, displayNames)
|
||||
}
|
||||
return displayNames.of(locale)
|
||||
}
|
||||
})()
|
||||
|
||||
const isChanging = ref(false)
|
||||
|
||||
const currentLocale = computed({
|
||||
get() {
|
||||
return $config.locale
|
||||
},
|
||||
async set(value) {
|
||||
if (isChanging.value) return
|
||||
|
||||
try {
|
||||
isChanging.value = true
|
||||
await changeLocale(value)
|
||||
} finally {
|
||||
isChanging.value = false
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="LanguageSwitcher">
|
||||
<h2 class="title">Playground language</h2>
|
||||
|
||||
<DropdownSelect
|
||||
class="locale-dropdown"
|
||||
name="locale"
|
||||
v-model="currentLocale"
|
||||
placeholder="Change language"
|
||||
:disabled="isChanging"
|
||||
:options="Array.from($locales).map(([{ tag }]) => tag)"
|
||||
:display-name="(locale: string) => getLocaleDisplayName(locale)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.LanguageSwitcher {
|
||||
padding-block: 18px;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.LanguageSwitcher .title {
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.LanguageSwitcher .locale-dropdown {
|
||||
width: 200px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@ -1,14 +1,50 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { localeDefinitions } from '@modrinth/omorphia-dev/locales/index.js'
|
||||
import { createPlugin } from '@vintl/vintl/plugin'
|
||||
import Omorphia from 'omorphia'
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { createVNode } from 'vue'
|
||||
import DemoContainer from './DemoContainer.vue'
|
||||
import LanguageSwitcher from './LanguageSwitcher.vue'
|
||||
|
||||
import './compat.scss'
|
||||
import './style.scss'
|
||||
|
||||
/** @type {import('vitepress').Theme} */
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
enhanceApp(ctx) {
|
||||
ctx.app.use(Omorphia)
|
||||
ctx.app.component('DemoContainer', DemoContainer)
|
||||
ctx.app.use(
|
||||
createPlugin({
|
||||
controllerOpts: {
|
||||
locales: Object.keys(localeDefinitions).map((tag) => ({ tag })),
|
||||
listen: {
|
||||
async localeload(event) {
|
||||
const locale = event.locale.tag
|
||||
if (!Object.hasOwn(localeDefinitions, locale)) {
|
||||
throw new Error(`Unknown locale: ${locale}`)
|
||||
}
|
||||
|
||||
try {
|
||||
const { messages } = await localeDefinitions[locale].importFunction()
|
||||
event.addMessages(messages)
|
||||
} catch (err) {
|
||||
console.error(`Failed to load locale: ${locale}`, err)
|
||||
}
|
||||
},
|
||||
},
|
||||
defaultMessageOrder: ['locale', 'descriptor'],
|
||||
},
|
||||
globalMixin: false,
|
||||
})
|
||||
)
|
||||
},
|
||||
Layout() {
|
||||
return createVNode(DefaultTheme.Layout, null, {
|
||||
'sidebar-nav-before'() {
|
||||
return createVNode(LanguageSwitcher)
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<button class="code" :class="{ copied }" title="Copy code to clipboard" @click="copyText">
|
||||
<button class="code" :class="{ copied }" :title="formatMessage(copiedMessage)" @click="copyText">
|
||||
<span>{{ text }}</span>
|
||||
<CheckIcon v-if="copied" />
|
||||
<ClipboardCopyIcon v-else />
|
||||
@ -8,6 +8,12 @@
|
||||
|
||||
<script setup>
|
||||
import { CheckIcon, ClipboardCopyIcon } from '@'
|
||||
import { useVIntl, defineMessage } from '@vintl/vintl'
|
||||
const copiedMessage = defineMessage({
|
||||
id: 'omorphia.component.copy.action.copy',
|
||||
defaultMessage: 'Copy code to clipboard',
|
||||
})
|
||||
const { formatMessage } = useVIntl()
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
5
locales/en-US/index.json
Normal file
5
locales/en-US/index.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"omorphia.component.copy.action.copy": {
|
||||
"defaultMessage": "Copy code to clipboard"
|
||||
}
|
||||
}
|
||||
14
package.json
14
package.json
@ -3,7 +3,8 @@
|
||||
"type": "module",
|
||||
"version": "0.6.5",
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"locales"
|
||||
],
|
||||
"module": "./dist/omorphia.js",
|
||||
"exports": {
|
||||
@ -11,6 +12,9 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/omorphia.js"
|
||||
},
|
||||
"./locales/*": {
|
||||
"import": "./locales/*"
|
||||
},
|
||||
"./dist/style.css": "./dist/style.css"
|
||||
},
|
||||
"scripts": {
|
||||
@ -20,7 +24,8 @@
|
||||
"fix": "eslint --fix --ext .js,.vue,.ts,.jsx,.tsx,.html,.vue . && prettier --write .",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:build": "vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs"
|
||||
"docs:preview": "vitepress preview docs",
|
||||
"intl:extract": "formatjs extract \"lib/**/*.{vue,ts,tsx,js,jsx,mts,cts,mjs,cjs}\" --ignore \"lib/**/*.d.ts\" --out-file locales/en-US/index.json --preserve-whitespace"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.3.0",
|
||||
@ -40,13 +45,18 @@
|
||||
"xss": "^1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formatjs/cli": "^6.2.1",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
"@vintl/unplugin": "^1.5.1",
|
||||
"@vintl/vintl": "^4.3.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.14.1",
|
||||
"glob": "^10.3.10",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-prefix-selector": "^1.16.0",
|
||||
"prettier": "^2.8.8",
|
||||
|
||||
749
pnpm-lock.yaml
generated
749
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
21
tsconfig.docs.json
Normal file
21
tsconfig.docs.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
|
||||
"target": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
|
||||
"allowJs": true,
|
||||
"allowArbitraryExtensions": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
|
||||
"emitDeclarationOnly": true
|
||||
},
|
||||
"include": [
|
||||
"docs/.vitepress/**/*.ts",
|
||||
"docs/.vitepress/**/*.js",
|
||||
"docs/.vitepress/**/*.vue",
|
||||
"locales/*.json"
|
||||
]
|
||||
}
|
||||
@ -27,5 +27,5 @@
|
||||
}
|
||||
},
|
||||
"include": ["lib/**/*.js", "lib/**/*.ts", "lib/**/*.d.ts", "lib/**/*.tsx", "lib/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.docs.json" }]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user