col.tr and aqt.utils.tr.
Please start by taking a look at the core documentation to see
how strings are presented to translators. Note how translators can not see the
areas of the code where a string is used, so comments are often required to help
the translators understand what they are translating.
Adding New Strings
As an example, imagine we want to add the string “You have x add-ons”. To add a new translatable string to the codebase, we first need to identify whether it belongs in thecore module (text likely to be used by all Anki clients),
or whether it is specific to the computer version (the desktop module).
Add-ons are only supported by the computer version, so we’ll want to use
the desktop module in this example.
- The English
corefiles are stored inftl/core - The English
desktopfiles are stored inftl/qt
ftl/qt/addons.ftl, and add one if no appropriate
one exists. Then we need to add the string to the file.
Each string needs a key that uniquely identifies it. It should start with
the same text as the filename, and then contain at least a few words separated
by hyphens. For example, we might add the following to the file:
count is 5, then the string will be “You have 5 add-ons.”. This means
it will look strange if count is 1 - we’d see “You have 1 add-ons.” instead
of “You have 1 add-on.”. We’ll need to add another string to cover the singular
case:
Accessing the New String
Once you’ve added one or more strings to the .ftl files, run Anki in the source tree as usual, which will compile the new strings, and make them accessible in Python/Typescript/Rust.Python
To resolve a string, you can use the following code:- Note how a snake_case() function has been automatically defined as part of the build process, with the correct arguments. These will be checked by the tests, to ensure you don’t accidentally use a missing string, or omit an argument/pass the wrong one.
- The generated function has a docstring based on the original text in the ftl file, so if you’re using an editor like PyCharm, you can hover the mouse over a function to see the text it will resolve to.
- Code in qt/ can use the
aqt.utils.tr()function, but code in pylib should usecol.tr()instead.
TypeScript
A global is available with automatically generated functions in camelCaps. Eg:Rust
The backend and collection structures have an I18n object in .tr, which can be called to get a translation, eg, in a Collection method:.to_string() or
.into() on it.
Legacy style
Anki versions before 2.1.44 used a single function and a separate constant instead. Eg, instead ofRepeated Content
Sometimes you will need multiple translations that have some text shared between them. For example, the search code has a list of separate error message, such as:Avoid HTML where possible
Translators may not have any development experience, and HTML can be difficult to read and translate correctly. Prefer plain text where possible, and when HTML is required or would make things much clearer, consider using markdown instead (see the search translations for an example of how this is done).Avoid Strings That Will Change
Avoid doing things like listing out a series of options in a string, if there’s any chance that list will change in the future. When the string later gets updated (and assuming people don’t forget to update it), it will need to be given a new ID so that translators become aware of it, and doing so will mean all the existing translations get invalidated until a translator has a chance to update them, which may take months and sometimes even years.Avoid Excess Strings
Please try to be conservative with the number of new strings you add, as translator time is precious, and the more strings included in Anki, the more overwhelming it can be for translators. If you need to display an error message for some obscure error that most people will never hit, it probably doesn’t need a translation.Add-ons
Add-ons can make use of existing strings in Anki, but if you wish to add new strings, you’ll need to add them to your add-on rather than Anki. How you approach translations in your add-on(s) is up to you:- If you don’t care about translations, the simplest solution is to use plain strings.
- The next-easiest option is to place all strings in a dict or python module, and accept pull requests from users that add a new language. You can then select the appropriate dict by looking at what anki.lang.currentLang is set to.
- If you want proper plural support, you’ll want to consider using gettext, though it is considerably more involved than the above solution. It requires separate build steps to extract strings from your source and build compiled versions of the strings. You can have translators email you a completed .po file, or send you a PR. There are online translation services like Launchpad and Crowdin that support gettext, but you would need to either manually upload and download files, or spend some time setting up scripts to do so.