Jelajahi Sumber

chore: Better fixups on lang tool and support

jamesread 7 bulan lalu
induk
melakukan
b97dd23abb

+ 20 - 7
frontend/resources/vue/App.vue

@@ -84,6 +84,7 @@ import { UserCircle02Icon } from '@hugeicons/core-free-icons'
 import { DashboardSquare01Icon } from '@hugeicons/core-free-icons'
 import logoUrl from '../../OliveTinLogo.png';
 import { useI18n } from 'vue-i18n';
+import combinedTranslations from '../../../lang/combined_output.json';
 
 const { t, locale } = useI18n();
 
@@ -129,13 +130,25 @@ const currentLanguageName = computed(() => {
     return availableLanguages[languagePreference.value] || languagePreference.value
 })
 
-function getBrowserLanguage() {
-    if (navigator.languages && navigator.languages.length > 0) {
-        return navigator.languages[0]
-    }
+function normalizeBrowserLanguage() {
+    const available = Object.keys(combinedTranslations.messages || {})
 
-    if (navigator.language) {
-        return navigator.language
+    if (navigator.languages && navigator.languages.length > 0) {
+        for (const candidate of navigator.languages) {
+            const lowerCandidate = candidate.toLowerCase()
+            
+            // Try exact match (case-insensitive)
+            const exact = available.find(locale => locale.toLowerCase() === lowerCandidate)
+            if (exact) {
+                return exact
+            }
+
+            // Try prefix match (e.g., "zh-CN" -> "zh-Hans-CN")
+            const prefix = available.find(locale => locale.toLowerCase().startsWith(lowerCandidate.split('-')[0] + '-'))
+            if (prefix) {
+                return prefix
+            }
+        }
     }
 
     return 'en'
@@ -235,7 +248,7 @@ function changeLanguage() {
     if (selectedLanguage.value === 'auto') {
         localStorage.removeItem('olivetin-language')
         languagePreference.value = 'auto'
-        window.i18n.locale.value = getBrowserLanguage()
+        window.i18n.locale.value = normalizeBrowserLanguage()
     } else {
         window.i18n.locale.value = selectedLanguage.value
         localStorage.setItem('olivetin-language', selectedLanguage.value)

+ 1 - 1
frontend/resources/vue/views/LogsListView.vue

@@ -7,7 +7,7 @@
               d="m19.6 21l-6.3-6.3q-.75.6-1.725.95T9.5 16q-2.725 0-4.612-1.888T3 9.5t1.888-4.612T9.5 3t4.613 1.888T16 9.5q0 1.1-.35 2.075T14.7 13.3l6.3 6.3zM9.5 14q1.875 0 3.188-1.312T14 9.5t-1.312-3.187T9.5 5T6.313 6.313T5 9.5t1.313 3.188T9.5 14" />
           </svg>
           <input :placeholder="t('search-filter')" v-model="searchText" />
-          <button title="Clear search filter" :disabled="!searchText" @click="clearSearch">
+          <button :title="t('logs.clear-filter')" :disabled="!searchText" @click="clearSearch">
             <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
               <path fill="currentColor"
                 d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z" />

+ 2 - 2
lang/README.md

@@ -16,7 +16,7 @@ your browser reports by opening the "Select Language" dialog from the footer.
 Internally, OliveTin uses the vue-i18n library for translations. This does support
 language pluralization and other advanced features. For docs, check the following;
 
-https://vue-i18n.intlify.dev/guide/essentials/pluralization.html
+[Vue i18n Pluralization Guide](https://vue-i18n.intlify.dev/guide/essentials/pluralization.html)
 
 The translation files are in YAML format. Each file contains key-value pairs. 
 
@@ -24,7 +24,7 @@ OliveTin developers then "process" these files into JSON format used for the app
 
 If you are able, it would be appreciated if you run `make` in the language directory 
 to process your language file before submitting a PR. This will ensure that the JSON
-file is up to date. If you don't understand how to do this, don't worry; just submit
+file is up-to-date. If you don't understand how to do this, don't worry; just submit
 the YAML file and the developers will take care of it.
 
 ## Contributing improvements

File diff ditekan karena terlalu besar
+ 0 - 0
lang/combined_output.json


+ 1 - 0
lang/de-DE.yaml

@@ -20,6 +20,7 @@ translations:
   logs.blocked: Blockiert
   logs.exit-code: Ausführungscode
   logs.completed: Abgeschlossen
+  logs.clear-filter: Suchfilter löschen
   return-to-index: Zurück zur Startseite
   search-filter: Filter aktuelle Seite
   language-dialog.title: Sprache auswählen

+ 1 - 0
lang/en.yaml

@@ -20,6 +20,7 @@ translations:
   logs.blocked: Blocked
   logs.exit-code: Exit code
   logs.completed: Completed
+  logs.clear-filter: Clear search filter
   return-to-index: Return to index
   search-filter: Filter current page
   language-dialog.title: Select Language

+ 3 - 2
lang/es-ES.yaml

@@ -11,15 +11,16 @@ translations:
   docs: Documentación
   logs.title: Registros
   logs.page-description: Esta es una lista de registros de acciones que han sido ejecutadas. Puede filtrar la lista por título de acción.
-  logs.timestamp: Timestamp
+  logs.timestamp: Marca de tiempo
   logs.action: Acción
   logs.metadata: Metadatos
-  logs.status: Status
+  logs.status: Estado
   logs.no-logs-to-display: No hay registros para mostrar.
   logs.timed-out: Tiempo agotado
   logs.blocked: Bloqueado
   logs.exit-code: Código de salida
   logs.completed: Completado
+  logs.clear-filter: Limpiar filtro de búsqueda
   return-to-index: Volver a la página principal
   search-filter: Filtrar página actual
   language-dialog.title: Seleccionar idioma

+ 3 - 2
lang/it-IT.yaml

@@ -11,15 +11,16 @@ translations:
   raise-issue: Segnala un problema su GitHub
   logs.title: Registri
   logs.page-description: Questa è una lista di registri delle azioni che sono state eseguite. Puoi filtrare la lista per titolo dell'azione.
-  logs.timestamp: Timestamp
+  logs.timestamp: Date e ora
   logs.action: Azione
   logs.metadata: Metadati
-  logs.status: Status
+  logs.status: Stato
   logs.no-logs-to-display: Non ci sono registri da mostrare.
   logs.timed-out: Tempo scaduto
   logs.blocked: Bloccato
   logs.exit-code: Codice di uscita
   logs.completed: Completato
+  logs.clear-filter: Cancella filtro di ricerca
   return-to-index: Torna alla pagina principale
   search-filter: Filtra la pagina corrente
   language-dialog.title: Seleziona lingua

+ 41 - 1
lang/main.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"gopkg.in/yaml.v3"
@@ -18,13 +19,16 @@ type LanguageFilev1 struct {
 }
 
 type CombinedTranslationsOutput struct {
+	Comment  string                       `json:"_comment"`
 	Messages map[string]map[string]string `json:"messages"`
 }
 
 func main() {
 	combinedContent := getCombinedLanguageContent()
 
-	jsonData, err := json.Marshal(combinedContent)
+	sortedContent := sortTranslations(combinedContent)
+
+	jsonData, err := json.MarshalIndent(sortedContent, "", "    ")
 
 	if err != nil {
 		log.Fatalf("Error marshalling combined language content: %v", err)
@@ -39,6 +43,41 @@ func main() {
 	log.Infof("Combined language content saved to combined_output.json")
 }
 
+// sortTranslations creates a new structure with sorted keys for deterministic output.
+func sortTranslations(input *CombinedTranslationsOutput) *CombinedTranslationsOutput {
+	sorted := &CombinedTranslationsOutput{
+		Comment:  input.Comment,
+		Messages: make(map[string]map[string]string),
+	}
+
+	// Sort language names
+	langNames := make([]string, 0, len(input.Messages))
+	for langName := range input.Messages {
+		langNames = append(langNames, langName)
+	}
+	sort.Strings(langNames)
+
+	// For each language, sort the translation keys
+	for _, langName := range langNames {
+		translations := input.Messages[langName]
+		sortedTranslations := make(map[string]string)
+
+		keys := make([]string, 0, len(translations))
+		for key := range translations {
+			keys = append(keys, key)
+		}
+		sort.Strings(keys)
+
+		for _, key := range keys {
+			sortedTranslations[key] = translations[key]
+		}
+
+		sorted.Messages[langName] = sortedTranslations
+	}
+
+	return sorted
+}
+
 func getLanguageDir() string {
 	dirsToSearch := []string{
 		"../lang",
@@ -53,6 +92,7 @@ func getLanguageDir() string {
 
 func getCombinedLanguageContent() *CombinedTranslationsOutput {
 	output := &CombinedTranslationsOutput{
+		Comment:  "This file is generated. Please re-generate this file using 'make' when you update a translation.",
 		Messages: make(map[string]map[string]string),
 	}
 

+ 2 - 1
lang/zh-Hans-CN.yaml

@@ -25,4 +25,5 @@ translations:
   logs.timed-out: 超时
   logs.blocked: 阻塞
   logs.exit-code: 退出代码
-  logs.completed: 完成
+  logs.completed: 完成
+  logs.clear-filter: 清除搜索筛选器

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini