cerca

lean forum software (pmc local branch)
git clone http://git.permacomputing.net/repos/cerca.git # read-only access
Log | Files | Refs | README | LICENSE

i18n.go (17728B)


      1 package i18n
      2 
      3 import (
      4 	"cerca/util"
      5 	"fmt"
      6 	"html/template"
      7 	"log"
      8 	"strings"
      9 )
     10 
     11 const toolURL = "https://github.com/cblgh/cerca/releases/tag/pwtool-v1"
     12 
     13 var English = map[string]string{
     14 	"About":    "about",
     15 	"Login":    "login",
     16 	"Logout":   "logout",
     17 	"Sort":     "sort",
     18 	"Enter":    "enter",
     19 	"Register": "register",
     20 	"Bottom":   "bottom",
     21 
     22 	"LoggedIn":    "logged in",
     23 	"NotLoggedIn": "Not logged in",
     24 	"LogIn":       "log in",
     25 	"GoBack":      "Go back",
     26 
     27 	"SortRecentPosts":   "recent posts",
     28 	"SortRecentThreads": "most recent threads",
     29 
     30 	"modlogResetPassword":      `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> reset a user's password`,
     31 	"modlogResetPasswordAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> reset <b> {{ .Data.RecipientUsername}}</b>'s password`,
     32 	"modlogRemoveUser":         `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> removed a user's account`,
     33 	"modlogMakeAdmin":          `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> made <b> {{ .Data.RecipientUsername}}</b> an admin`,
     34 	"modlogAddUser":            `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> manually registered an account for a new user`,
     35 	"modlogAddUserAdmin":       `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> manually registered an account for <b> {{ .Data.RecipientUsername }}</b>`,
     36 	"modlogDemoteAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> demoted <b> 
     37 	{{ if eq .Data.ActingUsername .Data.RecipientUsername }} themselves 
     38 	{{ else }} {{ .Data.RecipientUsername}} {{ end }}</b> from admin back to normal user`,
     39 	"modlogXProposedY":          `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> proposed: {{ .Data.Action }}`,
     40 	"modlogProposalMakeAdmin":   `Make <b> {{ .Data.RecipientUsername}}</b> admin`,
     41 	"modlogProposalDemoteAdmin": `Demote <b> {{ .Data.RecipientUsername}}</b> from role admin`,
     42 	"modlogProposalRemoveUser":  `Remove user <b> {{ .Data.RecipientUsername }} </b>`,
     43 	"modlogConfirm":             "{{ .Data.Action }} <i>confirmed by {{ .Data.ActingUsername }}</i>",
     44 	"modlogVeto":                "<s>{{ .Data.Action }}</s> <i>vetoed by {{ .Data.ActingUsername }}</i>",
     45 
     46 	"Admins":                        "admins",
     47 	"AdminVeto":                     "Veto",
     48 	"AdminConfirm":                  "Confirm",
     49 	"AdminForumAdministration":      "Forum Administration",
     50 	"AdminYou":                      "you!",
     51 	"AdminUsers":                    "Users",
     52 	"AdminNoAdmins":                 "There are no admins",
     53 	"AdminNoUsers":                  "There are no other users",
     54 	"AdminNoPendingProposals":       "There are no pending proposals",
     55 	"AdminAddNewUser":               "Add new user",
     56 	"AdminAddNewUserQuestion":       "Does someone wish attendence? You can ",
     57 	"AdminStepDown":                 "Step down",
     58 	"AdminStepDownExplanation":      "If you want to stop being an admin, you can",
     59 	"AdminViewPastActions":          "View past actions in the",
     60 	"ModerationLog":                 "moderation log",
     61 	"AdminDemote":                   "Demote",
     62 	"DeletedUser":                   "deleted user",
     63 	"RemoveAccount":                 "remove account",
     64 	"AdminMakeAdmin":                "Make admin",
     65 	"Submit":                        "Submit",
     66 	"AdminSelfConfirmationsHover":   "a week must pass before self-confirmations are ok",
     67 	"Proposal":                      "Proposal",
     68 	"PendingProposals":              "Pending Proposals",
     69 	"AdminSelfProposalsBecomeValid": "Date self-proposals become valid",
     70 	"AdminPendingExplanation": `Two admins are required for <i>making a user an admin</i>, <i>demoting an existing
     71 															admin</i>, or <i>removing a user</i>. The first proposes the action, the second confirms
     72 															(or vetos) it. If enough time elapses without a veto, the proposer may confirm their own
     73 															proposal.`,
     74 
     75 	"AdminAddUserExplanation":          "Register a new user account. After registering the account you will be given a generated password and instructions to pass onto the user.",
     76 	"AdminForumHasAdmins":              "The forum currently has the following admins",
     77 	"AdminOnlyLoggedInMayView":         "Only logged in users may view the forum's admins.",
     78 	"AdminPasswordSuccessInstructions": `Instructions: %s's password was set to: %s. After logging in, please change your password by going to /reset`,
     79 
     80 	"ModLogNoActions":           "there are no logged moderation actions",
     81 	"ModLogExplanation":         `This resource lists the moderation actions taken by the forum's administrators.`,
     82 	"ModLogExplanationAdmin":    `You are viewing this page as an admin, you will see slightly more details.`,
     83 	"ModLogOnlyLoggedInMayView": "Only logged in users may view the moderation log.",
     84 
     85 	"LoginNoAccount":       "Don't have an account yet? <a href='/register'>Register</a> one.",
     86 	"LoginFailure":         "<b>Failed login attempt:</b> incorrect password, wrong username, or a non-existent user.",
     87 	"LoginAlreadyLoggedIn": `You are already logged in. Would you like to <a href="/logout">log out</a>?`,
     88 
     89 	"Username":                  "username",
     90 	"Current":                   "current",
     91 	"New":                       "new",
     92 	"ChangePasswordDescription": "Use this page to change your password.",
     93 	"Password":                  "password",
     94 	"PasswordMin":               "Must be at least 9 characters long",
     95 	"PasswordForgot":            "Forgot your password?",
     96 
     97 	"Threads":           "threads",
     98 	"ThreadNew":         "new thread",
     99 	"ThreadThe":         "the thread",
    100 	"Index":             "index",
    101 	"GoBackToTheThread": "Go back to the thread",
    102 
    103 	"ThreadCreate":        "Create thread",
    104 	"Title":               "Title",
    105 	"Content":             "Content",
    106 	"Private":             "Private",
    107 	"Create":              "Create",
    108 	"TextareaPlaceholder": "Tabula rasa",
    109 
    110 	"PasswordReset":                   "reset password",
    111 	"PasswordResetSuccess":            "Reset password—success!",
    112 	"PasswordResetSuccessMessage":     "You reset your password!",
    113 	"PasswordResetSuccessLinkMessage": "Give it a try and",
    114 
    115 	"RegisterMessage":     "You already have an account (you are logged in with it).",
    116 	"RegisterLinkMessage": "Visit the",
    117 	"RegisterSuccess":     "registered successfully",
    118 
    119 	"ErrUnaccepted":        "Unaccepted request",
    120 	"ErrGeneric401":        "Unauthorized",
    121 	"ErrGeneric401Message": "You do not have permissions to perform this action.",
    122 	"ErrEdit404":           "Post not found",
    123 	"ErrEdit404Message":    "This post cannot be found for editing",
    124 	"ErrThread404":         "Thread not found",
    125 	"ErrThread404Message":  "The thread does not exist (anymore?)",
    126 	"ErrGeneric404":        "Page not found",
    127 	"ErrGeneric404Message": "The visited page does not exist (anymore?). Error code %d.",
    128 
    129 	"NewThreadMessage":            "Only members of this forum may create new threads",
    130 	"NewThreadLinkMessage":        "If you are a member,",
    131 	"NewThreadCreateError":        "Error creating thread",
    132 	"NewThreadCreateErrorMessage": "There was a database error when creating the thread, apologies.",
    133 	"PostEdit":                    "Post preview",
    134 	"PostPrivate":                 "This is a private thread, only logged-in users can see it and read its posts.",
    135 
    136 	"AriaPostMeta":          "Post meta",
    137 	"AriaDeletePost":        "Delete this post",
    138 	"AriaRespondIntoThread": "Respond into this thread",
    139 	"PromptDeleteQuestion":  "Delete post for all posterity?",
    140 	"Delete":                "delete",
    141 	"Edit":                  "edit",
    142 	"EditedAt":              "edited at",
    143 	"Post":                  "post",
    144 	"Save":                  "Save",
    145 	"Author":                "Author",
    146 	"Responded":             "responded",
    147 	"YourAnswer":            "Your answer",
    148 
    149 	"AriaHome":       "Home",
    150 	"ThreadStartNew": "Start a new thread",
    151 
    152 	"RegisterHTMLMessage": `You now have an account! Welcome. Visit the <a href="/">index</a> to read and reply to threads, or start a new one.`,
    153 
    154 	"RegisterVerificationCode":              "Your verification code is",
    155 	"RegisterVerificationInstructionsTitle": "Verification instructions",
    156 	"RegisterVerificationLink":              "Verification link",
    157 	"RegisterConductCodeBoxOne":             `I have refreshed my memory of the <a target="_blank" href="{{ .Data.Link }}">{{ .Data.Name }} Code of Conduct</a>`,
    158 	"RegisterConductCodeBoxTwo":             `Yes, I have actually <a target="_blank" href="{{ .Data.Link }}">read it</a>`,
    159 
    160 	"NewPassword":    "new password",
    161 	"ChangePassword": "change password",
    162 }
    163 
    164 var Swedish = map[string]string{
    165 	"About":    "om",
    166 	"Login":    "logga in",
    167 	"Logout":   "logga ut",
    168 	"Sort":     "sortera",
    169 	"Enter":    "skicka",
    170 	"Register": "registrera",
    171 	"Bottom":   "hoppa ner",
    172 
    173 	"LoggedIn":    "inloggad",
    174 	"NotLoggedIn": "Ej inloggad",
    175 	"LogIn":       "logga in",
    176 	"GoBack":      "Go back",
    177 
    178 	"SortRecentPosts":   "nyast poster",
    179 	"SortRecentThreads": "nyast trådar",
    180 
    181 	"LoginNoAccount":       "Saknar du konto? <a href='/register'>Skapa</a> ett.",
    182 	"LoginFailure":         "<b>Misslyckat inloggningsförsök:</b> inkorrekt lösenord, fel användernamn, eller obefintlig användare.",
    183 	"LoginAlreadyLoggedIn": `Du är redan inloggad. Vill du <a href="/logout">logga ut</a>?`,
    184 
    185 	"Username":                  "användarnamn",
    186 	"Current":                   "nuvarande",
    187 	"New":                       "nytt",
    188 	"ChangePasswordDescription": "På den här sidan kan du ändra ditt lösenord.",
    189 	"Password":                  "lösenord",
    190 	"PasswordMin":               "Måste vara minst 9 karaktärer långt",
    191 	"PasswordForgot":            "Glömt lösenordet?",
    192 
    193 	"Threads":           "trådar",
    194 	"ThreadNew":         "ny tråd",
    195 	"ThreadThe":         "tråden",
    196 	"Index":             "index",
    197 	"GoBackToTheThread": "Go back to the thread",
    198 
    199 	"ThreadCreate":        "Skapa en tråd",
    200 	"Title":               "Titel",
    201 	"Content":             "Innehåll",
    202 	"Create":              "Skapa",
    203 	"TextareaPlaceholder": "Tabula rasa",
    204 
    205 	"PasswordReset":                   "nollställ lösenord",
    206 	"PasswordResetSuccess":            "Nollställning av lösenord—lyckades!",
    207 	"PasswordResetSuccessMessage":     "Du har nollställt ditt lösenord!",
    208 	"PasswordResetSuccessLinkMessage": "Ge det ett försök och",
    209 
    210 	"RegisterMessage":     "Du har redan ett konto (du är inloggad med det).",
    211 	"RegisterLinkMessage": "Besök",
    212 	"RegisterSuccess":     "konto skapat",
    213 
    214 	"ErrUnaccepted":        "Ej accepterat request",
    215 	"ErrThread404":         "Tråd ej funnen",
    216 	"ErrThread404Message":  "Denna tråden finns ej (längre?)",
    217 	"ErrGeneric404":        "Sida ej funnen",
    218 	"ErrGeneric404Message": "Den besökta sidan finns ej (längre?). Felkod %d.",
    219 
    220 	"NewThreadMessage":            "Enbart medlemmarna av detta forum får skapa nya trådar",
    221 	"NewThreadLinkMessage":        "Om du är en medlem,",
    222 	"NewThreadCreateError":        "Fel uppstod vid trådskapning",
    223 	"NewThreadCreateErrorMessage": "Det uppstod ett databasfel under trådskapningen, ursäkta.",
    224 	"PostEdit":                    "Post preview",
    225 
    226 	"AriaPostMeta":          "Post meta",
    227 	"AriaDeletePost":        "Delete this post",
    228 	"AriaRespondIntoThread": "Respond into this thread",
    229 	"PromptDeleteQuestion":  "Radera post för alltid?",
    230 	"Delete":                "radera",
    231 	"Edit":                  "redigera",
    232 	"EditedAt":              "redigerat",
    233 	"Post":                  "post",
    234 	"Author":                "Författare",
    235 	"Responded":             "svarade",
    236 	"YourAnswer":            "Ditt svar",
    237 
    238 	"AriaHome":       "Hem",
    239 	"ThreadStartNew": "Starta ny tråd",
    240 
    241 	"RegisterHTMLMessage": `Du har nu ett konto! Välkommen. Besök <a href="/">trådindexet</a> för att läsa och svara på trådar, eller för att starta en ny.`,
    242 
    243 	"RegisterVerificationCode":              "Din verifikationskod är",
    244 	"RegisterVerificationInstructionsTitle": "Verification instructions",
    245 	"RegisterVerificationLink":              "Verificationsnyckel",
    246 	"RegisterConductCodeBoxOne":             `I have refreshed my memory of the <a target="_blank" href="{{ .Data.Link }}">{{ .Data.Name }} Code of Conduct</a>`,
    247 	"RegisterConductCodeBoxTwo":             `Yes, I have actually <a target="_blank" href="{{ .Data.Link }}">read it</a>`,
    248 
    249 	"PasswordResetDescription":      "På denna sida går vi igenom ett par steg för att säkert nollställa ditt lösenord—utan att behöva ta till mejl!",
    250 	"PasswordResetUsernameQuestion": "För de första: hur löd användarnamnet?",
    251 	"NewPassword":                   "nytt lösenord",
    252 	"ChangePassword":                "ändra lösenord",
    253 }
    254 
    255 var EspanolMexicano = map[string]string{
    256 	"About":    "acerca de",
    257 	"Login":    "loguearse",
    258 	"Logout":   "logout",
    259 	"Sort":     "sort",
    260 	"Register": "register",
    261 	"Enter":    "entrar",
    262 	"Bottom":   "bottom",
    263 
    264 	"LoggedIn":    "logged in",
    265 	"NotLoggedIn": "Not logged in",
    266 	"LogIn":       "log in",
    267 	"GoBack":      "Go back",
    268 
    269 	"SortRecentPosts":   "recent posts",
    270 	"SortRecentThreads": "most recent threads",
    271 
    272 	"LoginNoAccount":       "¿No tienes una cuenta? <a href='/register'>Registra</a> una. ",
    273 	"LoginFailure":         "<b>Failed login attempt:</b> incorrect password, wrong username, or a non-existent user.",
    274 	"LoginAlreadyLoggedIn": `You are already logged in. Would you like to <a href="/logout">log out</a>?`,
    275 
    276 	"Username":                  "usuarie",
    277 	"Current":                   "current",
    278 	"New":                       "new",
    279 	"ChangePasswordDescription": "Use this page to change your password.",
    280 	"Password":                  "contraseña",
    281 	"PasswordMin":               "Debe tener por lo menos 9 caracteres.",
    282 	"PasswordForgot":            "Olvidaste tu contraseña?",
    283 
    284 	"Threads":           "threads",
    285 	"ThreadNew":         "new thread",
    286 	"ThreadThe":         "the thread",
    287 	"Index":             "index",
    288 	"GoBackToTheThread": "Go back to the thread",
    289 
    290 	"ThreadCreate":        "Create thread",
    291 	"Title":               "Title",
    292 	"Content":             "Content",
    293 	"Create":              "Create",
    294 	"TextareaPlaceholder": "Tabula rasa",
    295 
    296 	"PasswordReset":                   "reset password",
    297 	"PasswordResetSuccess":            "Reset password—success!",
    298 	"PasswordResetSuccessMessage":     "You reset your password!",
    299 	"PasswordResetSuccessLinkMessage": "Give it a try and",
    300 
    301 	"RegisterMessage":     "You already have an account (you are logged in with it).",
    302 	"RegisterLinkMessage": "Visit the",
    303 	"RegisterSuccess":     "registered successfully",
    304 
    305 	"ErrUnaccepted":        "Unaccepted request",
    306 	"ErrThread404":         "Thread not found",
    307 	"ErrThread404Message":  "The thread does not exist (anymore?)",
    308 	"ErrGeneric404":        "Page not found",
    309 	"ErrGeneric404Message": "The visited page does not exist (anymore?). Error code %d.",
    310 
    311 	"NewThreadMessage":            "Only members of this forum may create new threads",
    312 	"NewThreadLinkMessage":        "If you are a member,",
    313 	"NewThreadCreateError":        "Error creating thread",
    314 	"NewThreadCreateErrorMessage": "There was a database error when creating the thread, apologies.",
    315 	"PostEdit":                    "Post preview",
    316 	"ThreadStartNew":              "Start a new thread",
    317 
    318 	"AriaPostMeta":          "Post meta",
    319 	"AriaDeletePost":        "Delete this post",
    320 	"AriaRespondIntoThread": "Respond into this thread",
    321 	"AriaHome":              "Home",
    322 	"PromptDeleteQuestion":  "Delete post for all posterity?",
    323 	"Delete":                "delete",
    324 	"Edit":                  "editar",
    325 	"EditedAt":              "editado a las",
    326 	"Post":                  "post",
    327 	"Save":                  "Save",
    328 	"Author":                "Author",
    329 	"Responded":             "responded",
    330 	"YourAnswer":            "Your answer",
    331 
    332 	"RegisterHTMLMessage": `You now have an account! Welcome. Visit the <a href="/">index</a> to read and reply to threads, or start a new one.`,
    333 
    334 	"RegisterVerificationCode":              "Your verification code is",
    335 	"RegisterVerificationInstructionsTitle": "Verification instructions",
    336 	"RegisterVerificationLink":              "Verification link",
    337 	"RegisterConductCodeBoxOne":             `I have refreshed my memory of the <a target="_blank" href="{{ .Data.Link }}">{{ .Data.Name }} Code of Conduct</a>`,
    338 	"RegisterConductCodeBoxTwo":             `Yes, I have actually <a target="_blank" href="{{ .Data.Link }}">read it</a>`,
    339 
    340 	"PasswordResetDescription":      "On this page we'll go through a few steps to securely reset your password—without resorting to any emails!",
    341 	"PasswordResetUsernameQuestion": "First up: what was your username?",
    342 	"NewPassword":                   "new password",
    343 	"ChangePassword":                "change password",
    344 }
    345 
    346 var translations = map[string]map[string]string{
    347 	"English":         English,
    348 	"EspañolMexicano": EspanolMexicano,
    349 	"Swedish":         Swedish,
    350 }
    351 
    352 type TranslationData struct {
    353 	Data interface{}
    354 }
    355 
    356 func (tr *Translator) TranslateWithData(key string, data TranslationData) string {
    357 	phrase := translations[tr.Language][key]
    358 	t, err := template.New(key).Parse(phrase)
    359 	ed := util.Describe("i18n translation")
    360 	ed.Check(err, "parse translation phrase")
    361 	sb := new(strings.Builder)
    362 	err = t.Execute(sb, data)
    363 	ed.Check(err, "execute template with data")
    364 	return sb.String()
    365 }
    366 
    367 func (tr *Translator) Translate(key string) string {
    368 	var empty TranslationData
    369 	return tr.TranslateWithData(key, empty)
    370 }
    371 
    372 type Translator struct {
    373 	Language string
    374 }
    375 
    376 func Init(lang string) Translator {
    377 	if _, ok := translations[lang]; !ok {
    378 		log.Fatalln(fmt.Sprintf("language '%s' is not translated yet", lang))
    379 	}
    380 	return Translator{lang}
    381 }
    382 
    383 // usage:
    384 // 	  tr := Init("EnglishSwedish")
    385 // 	  fmt.Println(tr.Translate("LoginNoAccount"))
    386 // 	  fmt.Println(tr.TranslateWithData("LoginDescription", Community{"Merveilles", "https://merveill.es"}))