Mirror of
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: link to lineNumbers from english keys

+67 -17
+4 -1
src/components/LocaleDetails.astro
··· 38 38 .filter((key) => key.status == "missing") 39 39 .map((missingKey) => ( 40 40 <li> 41 - <Link text={missingKey.name} href={missingKey.link} /> 41 + <Link 42 + text={missingKey.name} 43 + href={`${missingKey.link}#L${missingKey.lineNumber}`} 44 + /> 42 45 </li> 43 46 ))} 44 47 </ul>
+4 -1
src/components/StatusByKey.astro
··· 29 29 keyStatuses.map((keyStatus) => ( 30 30 <tr> 31 31 <td> 32 - <Link text={keyStatus.key.name} href={keyStatus.key.link} /> 32 + <Link 33 + text={keyStatus.key.name} 34 + href={`${keyStatus.key.link}#L${keyStatus.key.lineNumber}`} 35 + /> 33 36 </td> 34 37 {keyStatus.statuses.map((status) => ( 35 38 <td>
+5 -6
src/pages/index.astro
··· 7 7 8 8 <LunariaLayout title="Starlight Plugins Translation Tracker" {pluginsData}> 9 9 <p slot="description"> 10 - If you're interested in helping us translate various <a 11 - href="https://starlight.astro.build/resources/plugins/" 12 - >Starlight plugins</a 13 - > that need translations into one of the languages listed below, you've come 14 - to the right place! This auto-updating page always lists all the content that 15 - could use your help right now. 10 + This page tracks the current progress of translations for various plugins in 11 + the Starlight ecosystem. If you're using one of these plugins and would like 12 + to contribute translations for your language, feel free to check their 13 + repository for more details on how to contribute translations or reach out 14 + to the plugin author. 16 15 </p> 17 16 <p slot="description"> 18 17 In order to translate a missing key into your language, you need to create a
+1
src/schemas.ts
··· 50 50 const KeySchema = z.object({ 51 51 name: z.string(), 52 52 link: z.string(), 53 + lineNumber: z.number(), 53 54 }); 54 55 55 56 const LocaleKeysSchema = z.object({
+53 -9
src/utils.ts
··· 5 5 export type DataPerKey = { 6 6 key: string; 7 7 locales: Record<string, Status>; 8 + line: number; 8 9 }; 9 10 10 11 export type DataPerPlugin = { ··· 33 34 } 34 35 35 36 // Utility to fetch the remote .ts file and parse it 36 - async function fetchTranslationFile(url: string): Promise<TranslationMap> { 37 + async function fetchTranslationFile( 38 + url: string 39 + ): Promise<{ data: TranslationMap; enLineNumbers: Record<string, number> }> { 37 40 const res = await fetch(url); 38 41 const text = await res.text(); 42 + const lines = text.split("\n"); 39 43 40 - // Extract the JSON-ish object from the file 41 44 const match = text.match(/export const Translations\s*=\s*(\{[\s\S]*\});?/); 42 45 if (!match) throw new Error("Could not find Translations object in the file"); 43 46 44 - // Safely eval or use Function constructor (sandboxing highly recommended in real prod code) 45 47 const translationObject = new Function( 46 48 `return ${match[1]}` 47 49 )() as TranslationMap; 48 - return translationObject; 50 + 51 + // Line number extraction (for English keys only) 52 + const enLineNumbers: Record<string, number> = {}; 53 + let insideEn = false; 54 + 55 + for (let i = 0; i < lines.length; i++) { 56 + const line = lines[i]; 57 + 58 + if (!insideEn) { 59 + if (/en:\s*\{/.test(line)) { 60 + insideEn = true; 61 + } 62 + continue; 63 + } 64 + 65 + if (/^\s*\}/.test(line)) break; 66 + 67 + const keyMatch = line.match(/['"`]([^'"`]+)['"`]\s*:/); 68 + if (keyMatch) { 69 + const rawKey = keyMatch[1]; 70 + const normalized = normalizeKey(rawKey); 71 + enLineNumbers[normalized] = i + 1; 72 + } 73 + } 74 + 75 + return { data: translationObject, enLineNumbers }; 49 76 } 50 77 51 78 export async function processPlugins(): Promise<DataPerPlugin[]> { 52 79 const results: DataPerPlugin[] = []; 53 80 54 81 for (const plugin of DashboardData.plugins) { 55 - const data = await fetchTranslationFile(plugin.translationFileLinkRaw); 82 + const { data, enLineNumbers } = await fetchTranslationFile( 83 + plugin.translationFileLinkRaw 84 + ); 56 85 57 86 const defaultLang = "en"; 58 - const defaultKeys = new Set(Object.keys(data[defaultLang] || {})); 59 - const normalizedDefaultKeys = new Set([...defaultKeys].map(normalizeKey)); 87 + const defaultKeys = Object.keys(data[defaultLang] || {}); 88 + const normalizedDefaultKeys = defaultKeys.map(normalizeKey); 60 89 const allLocales = DashboardData.locales.map((l) => l.lang); 61 90 62 91 const keysStatus: DataPerKey[] = []; 63 92 64 93 for (const key of normalizedDefaultKeys) { 94 + const line = enLineNumbers[key] ?? null; 65 95 const localesStatus: Record<string, Status> = {}; 66 96 67 97 for (const lang of allLocales) { ··· 73 103 localesStatus[lang] = hasTranslation ? "done" : "missing"; 74 104 } 75 105 76 - keysStatus.push({ key, locales: localesStatus }); 106 + keysStatus.push({ 107 + key, 108 + line, 109 + locales: localesStatus, 110 + }); 77 111 } 78 112 79 113 results.push({ ··· 124 158 key: { 125 159 name: entry.key, 126 160 link: plugin.translationFileLink, 161 + lineNumber: entry.line, 127 162 }, 128 163 statuses: [], 129 164 }); ··· 170 205 ): LocaleKeys[] { 171 206 const localeMap = new Map< 172 207 string, 173 - { locale: Locale; keys: { name: string; link: string; status: Status }[] } 208 + { 209 + locale: Locale; 210 + keys: { 211 + name: string; 212 + link: string; 213 + lineNumber: number; 214 + status: Status; 215 + }[]; 216 + } 174 217 >(); 175 218 176 219 for (const { key, statuses } of keyStatuses) { ··· 180 223 const keyEntry = { 181 224 name: key.name, 182 225 link: key.link, 226 + lineNumber: key.lineNumber, 183 227 status, 184 228 }; 185 229