diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 000000000..2817cde42 --- /dev/null +++ b/crowdin.yml @@ -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% diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 4b123f485..082e30b89 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -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'], }, diff --git a/docs/.vitepress/env.d.ts b/docs/.vitepress/env.d.ts new file mode 100644 index 000000000..7fa93f572 --- /dev/null +++ b/docs/.vitepress/env.d.ts @@ -0,0 +1,15 @@ +/// + +declare module '@modrinth/omorphia-dev/locales/index.js' { + interface LocaleExport { + messages: Record + } + + interface LocaleDefinition { + importFunction(): Promise + } + + const localeDefinitions: Partial> + + export { localeDefinitions } +} diff --git a/docs/.vitepress/theme/LanguageSwitcher.vue b/docs/.vitepress/theme/LanguageSwitcher.vue new file mode 100644 index 000000000..5f4f87d75 --- /dev/null +++ b/docs/.vitepress/theme/LanguageSwitcher.vue @@ -0,0 +1,73 @@ + + + diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js index 54d291871..ef4c92a5d 100644 --- a/docs/.vitepress/theme/index.js +++ b/docs/.vitepress/theme/index.js @@ -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) + }, + }) }, } diff --git a/lib/components/base/CopyCode.vue b/lib/components/base/CopyCode.vue index db5f409c6..d34753fa2 100644 --- a/lib/components/base/CopyCode.vue +++ b/lib/components/base/CopyCode.vue @@ -1,5 +1,5 @@