diff --git a/webapp/src/app/[lang]/page.tsx b/webapp/src/app/[lang]/page.tsx index dcd310e9..9a0ce79e 100644 --- a/webapp/src/app/[lang]/page.tsx +++ b/webapp/src/app/[lang]/page.tsx @@ -53,7 +53,7 @@ export default function Home({ params }: { params: { lang: string } }) { key={msg.id} message={msg} onSave={async (text) => { - const res = await fetch( + await fetch( `/api/translations/${params.lang}/${msg.id}`, { body: JSON.stringify({ @@ -66,7 +66,6 @@ export default function Home({ params }: { params: { lang: string } }) { }, ); - await res.json(); setTranslations((cur) => ({ ...cur, [msg.id]: text, diff --git a/webapp/src/app/api/Store.ts b/webapp/src/app/api/Store.ts new file mode 100644 index 00000000..3eb5f2dd --- /dev/null +++ b/webapp/src/app/api/Store.ts @@ -0,0 +1,57 @@ +/* global globalThis */ +import fs from 'fs/promises'; +import { parse } from 'yaml'; +import { envVarNotFound, logDebug } from '@/utils/util'; +import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git'; + +const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); +const MAIN_BRANCH = 'main'; + +export class Store { + public static async getLanguage(lang: string) { + let languages: Map>; + if (!globalThis.languages) { + logDebug('Initializing languages'); + const options: Partial = { + baseDir: REPO_PATH, + binary: 'git', + maxConcurrentProcesses: 1, + trimmed: false, + }; + const git: SimpleGit = simpleGit(options); + logDebug('git checkout main pull...'); + await git.checkout(MAIN_BRANCH); + logDebug('git pull...'); + await git.pull(); + logDebug('git done checkout main branch and pull'); + languages = new Map>(); + globalThis.languages = languages; + } else { + logDebug('find languages in Memory'); + languages = globalThis.languages; + } + + let translations: Record; + if (!languages.has(lang)) { + logDebug('read language[' + lang +'] from file'); + // TODO: read this from .lyra.yml setting file in client repo + const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; + + const yamlBuf = await fs.readFile(yamlPath); + // TODO: change parsing to be flattened map of key to value, instead of object + // so key will be like 'key1.key2.key3' and value will be 'translated text' + // this will reduce the cost of looping for the object every time we need to save a message + translations = parse(yamlBuf.toString()) as Record; + languages.set(lang, translations); + } else { + logDebug('read language [' + lang + '] from Memory'); + translations = languages.get(lang) ?? Store.throwLangNotFound(lang); + } + + return translations; + } + + private static throwLangNotFound(lang: string): never { + throw new Error(`Language ${lang} not found`); + } +} diff --git a/webapp/src/app/api/languages.ts b/webapp/src/app/api/languages.ts deleted file mode 100644 index cf49905a..00000000 --- a/webapp/src/app/api/languages.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* global globalThis */ -import fs from 'fs/promises'; -import { parse } from 'yaml'; -import { envVarNotFound, logDebug } from '@/utils/util'; -import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git'; - -const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); -const MAIN_BRANCH = 'main'; - -export async function getLanguage(lang: string) { - let languages: Map>; - if (!globalThis.languages) { - logDebug('Initializing languages'); - const options: Partial = { - baseDir: REPO_PATH, - binary: 'git', - maxConcurrentProcesses: 1, - trimmed: false, - }; - const git: SimpleGit = simpleGit(options); - logDebug('git checkout main pull...'); - await git.checkout(MAIN_BRANCH); - logDebug('git pull...'); - await git.pull(); - logDebug('git done checkout main branch and pull'); - languages = new Map>(); - globalThis.languages = languages; - } else { - logDebug('read languages from globalThis'); - languages = globalThis.languages; - } - - let translations: Record; - if (!languages.has(lang)) { - logDebug('read languages from file'); - const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; - - const yamlBuf = await fs.readFile(yamlPath); - translations = parse(yamlBuf.toString()) as Record; - languages.set(lang, translations); - } else { - logDebug('read languages from Memory'); - translations = languages.get(lang) ?? throwLangNotFound(lang); - } - - return translations; -} - -function throwLangNotFound(lang: string): never { - throw new Error(`Language ${lang} not found`); -} diff --git a/webapp/src/app/api/messages/route.ts b/webapp/src/app/api/messages/route.ts index ff93e4b2..ef44c01c 100644 --- a/webapp/src/app/api/messages/route.ts +++ b/webapp/src/app/api/messages/route.ts @@ -10,10 +10,14 @@ const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH'); export async function GET() { const messages: MessageData[] = []; + // TODO: read path or src from .lyra.yml setting file in client repo for await (const item of getMessageFiles(REPO_PATH + '/src')) { messages.push(...readTypedMessages(item)); } + // TODO: change data instruction to be a map of key to value, instead of object + // message id is the key, and value is an object with default and params + // example: { 'key1.key2.key3': { default: 'default text', params: [] }} return NextResponse.json({ data: messages, }); diff --git a/webapp/src/app/api/pull-request/route.ts b/webapp/src/app/api/pull-request/route.ts index 5ae236e0..33ca5594 100644 --- a/webapp/src/app/api/pull-request/route.ts +++ b/webapp/src/app/api/pull-request/route.ts @@ -39,6 +39,7 @@ export async function POST() { const languages = globalThis.languages; for (const lang of languages.keys()) { const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`; + // TODO: when language obj become a map of key to value, then it need to be converted to object before stringify const yamlOutput = stringify(languages.get(lang), { doubleQuotedAsJSON: true, singleQuote: true, diff --git a/webapp/src/app/api/translations/[lang]/[msgId]/route.ts b/webapp/src/app/api/translations/[lang]/[msgId]/route.ts index 0505d1da..148e9454 100644 --- a/webapp/src/app/api/translations/[lang]/[msgId]/route.ts +++ b/webapp/src/app/api/translations/[lang]/[msgId]/route.ts @@ -1,4 +1,4 @@ -import { getLanguage } from '@/app/api/languages'; +import { Store } from '@/app/api/Store'; import { NextRequest, NextResponse } from 'next/server'; export async function PUT( @@ -15,7 +15,7 @@ export async function PUT( const { text } = payload; const objKeyPath = msgId.split('.'); - let curObj = await getLanguage(lang); + let curObj = await Store.getLanguage(lang); objKeyPath.forEach((key, index) => { if (index == objKeyPath.length - 1) { curObj[key] = text; diff --git a/webapp/src/app/api/translations/[lang]/route.ts b/webapp/src/app/api/translations/[lang]/route.ts index a6b0bbfb..56e7dace 100644 --- a/webapp/src/app/api/translations/[lang]/route.ts +++ b/webapp/src/app/api/translations/[lang]/route.ts @@ -1,4 +1,4 @@ -import { getLanguage } from '@/app/api/languages'; +import { Store } from '@/app/api/Store'; import { NextRequest, NextResponse } from 'next/server'; export async function GET( @@ -6,7 +6,7 @@ export async function GET( context: { params: { lang: string; msgId: string } }, ) { const lang = context.params.lang; - const langObj = await getLanguage(lang); + const langObj = await Store.getLanguage(lang); const flattenLangObj = flattenObject(langObj); return NextResponse.json({