Embed
Wording and language
Every word the widgets say is replaceable, and none of it is a translation file you have to keep in step with ours. Pass the strings you want in the language you want them in.
The text option
text merges over the widgets' own wording. Name only the keys you are changing; everything you leave out keeps its default, so a new key added in a later release arrives in English rather than as an empty label.
VoteFirst.init({
project: 'your-project-slug',
key: 'your-embed-key',
locale: 'de-DE',
text: {
vote: 'Abstimmen',
voted: 'Abgestimmt',
votes: 'Stimmen',
more: 'Mehr anzeigen',
empty: 'Noch nichts hier',
suggestTitle: 'Feature vorschlagen'
}
});
Three of the strings carry a placeholder, which is replaced wherever you put it: {n} is a count of voters, {pct} is a whole number percentage, and {field} is the name of the field a form is complaining about.
Dates and numbers
locale takes a BCP 47 tag and decides how a release date and a vote count are written. Left out, both follow the reader's own browser, which is usually what you want on a public page and not what you want on a page that is only ever in one language.
VoteFirst.init({ project: '...', key: '...', locale: 'de-DE' });
// 17. Aug. 2026, and 1.204 rather than 1,204
A tag the reader's browser does not know falls back to a plain date rather than failing, so an unusual one costs nothing.
Every key
Grouped by where they appear. Anything not listed here is not a string the widgets say.
Shared
| Key | Default |
|---|---|
loading | Loading |
empty | Nothing here yet |
retry | Try again |
errorGeneric | Something went wrong |
more | Show more |
cancel | Cancel |
close | Close |
poweredBy | Powered by VoteFirst |
Cards and vote controls
| Key | Default |
|---|---|
vote | Vote |
voted | Voted |
votes | votes |
comment | comment, used when there is one |
comments | comments |
views | Views |
shareOfVoters | {pct}% of voters, on the Most Wanted list |
developerResponse | Developer response, above the answer the project owner wrote |
denialReason | Denial reason, the same block on a declined feature |
votingClosed | Voting is closed here, which is what a vote button or a badge says when the column takes no votes and the count is hidden as well |
Changelog
The two vocabularies below both reach this field. Releases filed before the newer names existed still carry the older pair, and rows of both kinds sit in the same project, so both are named here.
| Key | Default |
|---|---|
featured | Featured |
requestedBy | Requested by {n} voters |
type_new | New |
type_feature | New Feature |
type_improvement | Improvement |
type_fix | Fix |
type_bug_fix | Bug Fix |
type_security | Security |
type_breaking_change | Breaking Change |
The request a card opens
Clicking a card opens that request in place of the board, with its counts, its progress and its thread. These are the words that view adds.
| Key | Default |
|---|---|
back | Back, on the control that returns to the board |
upvotes | Upvotes |
progress | Progress |
The comment thread
| Key | Default |
|---|---|
commentsTitle | Comments |
commentOne | Comment, used as the heading when there is one |
commentNone | No comments yet |
commentHint | Add a comment... |
commentSubmit | Post Comment |
sortNewest, sortOldest, sortTop | Newest, Oldest, Top |
like | Like |
reply | Reply |
replyHint | Write a reply |
replySubmit | Reply, on the button that sends one |
remove | Delete, on a comment this visitor left |
you | You, beside this visitor's own name |
anonymous | Anonymous, for a comment whose author has no name |
roleOwner, roleEditor | Owner, Editor, on a comment from your own team |
What a refusal says
A refused call is written for the visitor rather than for you. The server's own sentence names routes and headers and never reaches the page; this is what does. The console still gets the real one, and the error object carries the code your handler should branch on.
| Key | Default, and the code behind it |
|---|---|
errorRefused | That is not allowed here. not_allowed |
errorBanned | You cannot post on this board. voter_banned |
errorBusy | Too many requests just now. Try again in a moment. rate_limited |
errorGone | That is no longer here. not_found |
errorDone | That has already been done. already_exists |
errorSending | Still sending. Give it a moment. idempotency_in_progress |
errorSetup | This is not set up correctly. A wrong key, a project slug that names nothing, or an origin your allowlist does not carry, which are your mistakes rather than the visitor's |
tooLong | {field} is too long. Shorten it and try again. |
The two popups
Every key below exists twice, once with each prefix, so the suggestion form and the problem report form can say different things. Where the default is empty the line is not drawn at all until you give it something.
| Key | Default |
|---|---|
suggestTitle | Suggest a Feature |
suggestIntro | Empty |
suggestHeading | Title |
suggestHeadingHint | A short, descriptive title |
suggestDescription | Description |
suggestDescriptionHint | Describe the feature you'd like to see... |
suggestSubmit | Submit |
suggestDone | Thanks. Your suggestion has been recorded. |
suggestHeldForReview | Shown instead of nothing when the project reviews new submissions before they appear |
reportTitle | Report a problem |
reportIntro | Empty |
reportHeading | What happened |
reportHeadingHint | A short, descriptive title |
reportDescription | Steps, and what you expected instead |
reportDescriptionHint | What you did, what happened, and what you expected instead |
reportSubmit | Submit |
reportDone | Thanks. Your report has been recorded. |
reportHeldForReview | The same sentence as above, in the report's own noun |
suggestClosed | This project is not accepting suggestions right now. A popup has no button to withhold, so a project that takes neither is telling rather than refusing |
reportClosed | This project is not accepting reports right now. |
The same wording can be given to one popup instead of to every widget, which is what you want when two buttons on the same page open the same form for different reasons.
VoteFirst.suggest({
trigger: '#suggest-btn',
title: 'Tell us what to build',
intro: 'We read all of these.',
headingLabel: 'In one line',
headingHint: 'Dark mode',
descriptionLabel: 'Why it would help',
descriptionHint: 'What you are doing when you need it',
submitLabel: 'Send it',
doneMessage: 'Got it. Thank you.'
});
| Option | Replaces |
|---|---|
title | suggestTitle or reportTitle |
intro | suggestIntro or reportIntro |
headingLabel, headingHint | The first field's label and placeholder |
descriptionLabel, descriptionHint | The second field's label and placeholder |
submitLabel | suggestSubmit or reportSubmit |
doneMessage | suggestDone or reportDone |