| 1 | # Core GIT Translations |
| 2 | |
| 3 | This directory holds the translations for the core of Git. This document |
| 4 | describes how you can contribute to the effort of enhancing the language |
| 5 | coverage and maintaining the translation. |
| 6 | |
| 7 | The localization (l10n) coordinator, Jiang Xin <worldhello.net@gmail.com>, |
| 8 | coordinates our localization effort in the l10n coordinator repository: |
| 9 | |
| 10 | https://github.com/git-l10n/git-po/ |
| 11 | |
| 12 | We will use XX as an alias to refer to the language translation code in |
| 13 | the following paragraphs, for example we use "po/XX.po" to refer to the |
| 14 | translation file for a specific language. But this doesn't mean that |
| 15 | the language code has only two letters. The language code can be in one |
| 16 | of two forms: "ll" or "ll\_CC". Here "ll" is the ISO 639 two-letter |
| 17 | language code and "CC" is the ISO 3166 two-letter code for country names |
| 18 | and subdivisions. For example: "de" for German language code, "zh\_CN" |
| 19 | for Simplified Chinese language code. |
| 20 | |
| 21 | |
| 22 | ## Contributing to an existing translation |
| 23 | |
| 24 | As a contributor for a language XX, you should first check TEAMS file in |
| 25 | this directory to see whether a dedicated repository for your language XX |
| 26 | exists. Fork the dedicated repository and start to work if it exists. |
| 27 | |
| 28 | Sometime, contributors may find that the translations of their Git |
| 29 | distributions are quite different with the translations of the |
| 30 | corresponding version from Git official. This is because some Git |
| 31 | distributions (such as from Ubuntu, etc.) have their own l10n workflow. |
| 32 | For this case, wrong translations should be reported and fixed through |
| 33 | their workflows. |
| 34 | |
| 35 | |
| 36 | ## Creating a new language translation |
| 37 | |
| 38 | If you are the first contributor for the language XX, please fork this |
| 39 | repository, prepare and/or update the translated message file "po/XX.po" |
| 40 | (described later), and ask the l10n coordinator to pull your work. |
| 41 | |
| 42 | If there are multiple contributors for the same language, please first |
| 43 | coordinate among yourselves and nominate the team leader for your |
| 44 | language, so that the l10n coordinator only needs to interact with one |
| 45 | person per language. |
| 46 | |
| 47 | |
| 48 | ## Translation Process Flow |
| 49 | |
| 50 | The overall data-flow looks like this: |
| 51 | |
| 52 | +-------------------+ +------------------+ |
| 53 | | Git source code | ----(2)---> | L10n coordinator | |
| 54 | | repository | <---(5)---- | repository | |
| 55 | +-------------------+ +------------------+ |
| 56 | | | ^ |
| 57 | (1) (3) (4) |
| 58 | V v | |
| 59 | +----------------------------------+ |
| 60 | | Language Team XX | |
| 61 | +----------------------------------+ |
| 62 | |
| 63 | - Translatable strings are marked in the source file. |
| 64 | - Language teams can start translation iterations at any time, even |
| 65 | before the l10n window opens: |
| 66 | |
| 67 | + Pull from the master branch of the source (1) |
| 68 | + Update the message file by running "make po-update PO\_FILE=po/XX.po" |
| 69 | + Translate the message file "po/XX.po" |
| 70 | |
| 71 | - The L10n coordinator pulls from source and announces the l10n window |
| 72 | open (2) |
| 73 | - Language team pulls from the l10n coordinator, starts another |
| 74 | translation iteration against the l10n coordinator's tree (3) |
| 75 | |
| 76 | + Run "git pull --rebase" from the l10n coordinator |
| 77 | + Update the message file by running "make po-update PO\_FILE=po/XX.po" |
| 78 | + Translate the message file "po/XX.po" |
| 79 | + Squash trivial l10n git commits using "git rebase -i" |
| 80 | |
| 81 | - Language team sends pull request to the l10n coordinator (4) |
| 82 | - L10n coordinator checks and merges |
| 83 | - L10n coordinator asks the result to be pulled (5). |
| 84 | |
| 85 | |
| 86 | ## Dynamically generated POT files |
| 87 | |
| 88 | POT files are templates for l10n contributors to create or update their |
| 89 | translation files. We used to have the "po/git.pot" file which was |
| 90 | generated by the l10n coordinator, but this file had been removed from |
| 91 | the tree. |
| 92 | |
| 93 | The two POT files "po/git.pot" and "po/git-core.pot" can be created |
| 94 | dynamically when necessary. |
| 95 | |
| 96 | L10n contributors use "po/git.pot" to prepare translations for their |
| 97 | languages, but they are not expected to modify it. The "po/git.pot" file |
| 98 | can be generated manually with the following command: |
| 99 | |
| 100 | ```shell |
| 101 | make po/git.pot |
| 102 | ``` |
| 103 | |
| 104 | The "po/git-core.pot" file is the template for core translations. A core |
| 105 | translation is the minimum set of work necessary to complete a |
| 106 | translation of a new language. Since there are more than 5000 messages |
| 107 | in the full set of template message file "po/git.pot" that need to be |
| 108 | translated, this is not a piece of cake for new language contributors. |
| 109 | |
| 110 | The "core" template file "po/git-core.pot" can be generated manually |
| 111 | by running: |
| 112 | |
| 113 | ```shell |
| 114 | make po/git-core.pot |
| 115 | ``` |
| 116 | |
| 117 | |
| 118 | ## Initializing a "XX.po" file |
| 119 | |
| 120 | (This is done by the language teams). |
| 121 | |
| 122 | If your language XX does not have translated message file "po/XX.po" yet, |
| 123 | you add a translation for the first time by running: |
| 124 | |
| 125 | ```shell |
| 126 | make po-init PO_FILE=po/XX.po |
| 127 | ``` |
| 128 | |
| 129 | where XX is the locale, e.g. "de", "is", "pt\_BR", "zh\_CN", etc. |
| 130 | |
| 131 | The newly generated message file "po/XX.po" is based on the core pot |
| 132 | file "po/git-core.pot", so it contains only a minimal set of messages |
| 133 | and it's a good start for a new language contribution. |
| 134 | |
| 135 | Once you are done testing the translation (see below), commit the result |
| 136 | and ask the l10n coordinator to pull from you. |
| 137 | |
| 138 | |
| 139 | ## Updating a "XX.po" file |
| 140 | |
| 141 | (This is done by the language teams). |
| 142 | |
| 143 | If you are replacing translation strings in an existing "XX.po" file to |
| 144 | improve the translation, just edit the file. |
| 145 | |
| 146 | If you want to find new translatable strings in source files of upstream |
| 147 | repository and propagate them to your "po/XX.po", run command: |
| 148 | |
| 149 | ```shell |
| 150 | make po-update PO_FILE=po/XX.po |
| 151 | ``` |
| 152 | |
| 153 | It will: |
| 154 | |
| 155 | - Call "make po/git.pot" to generate new "po/git.pot" file |
| 156 | - Call "msgmerge --add-location --backup=off -U po/XX.po po/git.pot" |
| 157 | to update your "po/XX.po" |
| 158 | - The "--add-location" option for msgmerge will add location lines, |
| 159 | and these location lines will help translation tools to locate |
| 160 | translation context easily. |
| 161 | |
| 162 | |
| 163 | ## Fuzzy translation |
| 164 | |
| 165 | Fuzzy translation is a translation marked by comment "fuzzy" to let you |
| 166 | know that the translation is out of date because the "msgid" has been |
| 167 | changed. A fuzzy translation will be ignored when compiling using "msgfmt". |
| 168 | Fuzzy translation can be marked by hands, but for most cases they are |
| 169 | marked automatically when running "msgmerge" to update your "XX.po" file. |
| 170 | |
| 171 | After fixing the corresponding translation, you must remove the "fuzzy" |
| 172 | tag in the comment. |
| 173 | |
| 174 | |
| 175 | ## Testing your changes |
| 176 | |
| 177 | (This is done by the language teams, after creating or updating "XX.po" file). |
| 178 | |
| 179 | Before you submit your changes go back to the top-level and do: |
| 180 | |
| 181 | ```shell |
| 182 | make |
| 183 | ``` |
| 184 | |
| 185 | On systems with GNU gettext (i.e. not Solaris) this will compile your |
| 186 | changed PO file with `msgfmt --check`, the --check option flags many |
| 187 | common errors, e.g. missing printf format strings, or translated |
| 188 | messages that deviate from the originals in whether they begin/end |
| 189 | with a newline or not. |
| 190 | |
| 191 | L10n coordinator will check your contributions using a helper program |
| 192 | (see "PO helper" section below): |
| 193 | |
| 194 | ```shell |
| 195 | git-po-helper check-po po/XX.po |
| 196 | git-po-helper check-commits <rev-list-opts> |
| 197 | ``` |
| 198 | |
| 199 | |
| 200 | ## Preparing a "XX.po" file for commit |
| 201 | |
| 202 | Once you are done testing the translation, it's better to commit a |
| 203 | location-less "po/XX.po" file to save repository space and make a |
| 204 | user-friendly patch for review. |
| 205 | |
| 206 | To save a location-less "po/XX.po" automatically in the repository, |
| 207 | follow these steps: |
| 208 | |
| 209 | First, check which filter is configured for your "po/XX.po" file: |
| 210 | |
| 211 | ``` |
| 212 | git check-attr filter po/XX.po |
| 213 | ``` |
| 214 | |
| 215 | The filter configuration is defined in the "po/.gitattributes" file. |
| 216 | |
| 217 | Then define the driver for the filter. Most languages use the |
| 218 | "gettext-no-location" clean filter, which strips out both filenames and line |
| 219 | numbers from location comments. To set this up, run the following command: |
| 220 | |
| 221 | ```shell |
| 222 | git config --global filter.gettext-no-location.clean \ |
| 223 | "msgcat --no-location -" |
| 224 | ``` |
| 225 | |
| 226 | Some PO files use the "gettext-no-line-number" clean filter, which keeps |
| 227 | filenames but strips line numbers. This filter requires gettext 0.20 or |
| 228 | later. The only benefit is being able to locate source files from location |
| 229 | comments when the .po file is not updated from the POT via `make po-update`. |
| 230 | |
| 231 | ```shell |
| 232 | git config --global filter.gettext-no-line-number.clean \ |
| 233 | "msgcat --add-location=file -" |
| 234 | ``` |
| 235 | |
| 236 | You're now ready to ask the l10n coordinator to pull from you. |
| 237 | |
| 238 | |
| 239 | ## Marking strings for translation |
| 240 | |
| 241 | (This is done by the core developers). |
| 242 | |
| 243 | Before strings can be translated they first have to be marked for |
| 244 | translation. |
| 245 | |
| 246 | Git uses an internationalization interface that wraps the system's |
| 247 | gettext library, so most of the advice in your gettext documentation |
| 248 | (on GNU systems `info gettext` in a terminal) applies. |
| 249 | |
| 250 | General advice: |
| 251 | |
| 252 | - Don't mark everything for translation, only strings which will be |
| 253 | read by humans (the porcelain interface) should be translated. |
| 254 | |
| 255 | The output from Git's plumbing utilities will primarily be read by |
| 256 | programs and would break scripts under non-C locales if it was |
| 257 | translated. Plumbing strings should not be translated, since |
| 258 | they're part of Git's API. |
| 259 | |
| 260 | - Adjust the strings so that they're easy to translate. Most of the |
| 261 | advice in `info '(gettext)Preparing Strings'` applies here. |
| 262 | |
| 263 | - Strings referencing numbers of items may need to be split into singular and |
| 264 | plural forms; see the Q\_() wrapper in the C sub-section below for an |
| 265 | example. |
| 266 | |
| 267 | - If something is unclear or ambiguous you can use a "TRANSLATORS" |
| 268 | comment to tell the translators what to make of it. These will be |
| 269 | extracted by xgettext(1) and put in the "po/\*.po" files, e.g. from |
| 270 | git-am.sh: |
| 271 | |
| 272 | ```shell |
| 273 | # TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a] |
| 274 | # in your translation. The program will only accept English |
| 275 | # input at this point. |
| 276 | gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " |
| 277 | ``` |
| 278 | |
| 279 | Or in C, from builtin/revert.c: |
| 280 | |
| 281 | ```c |
| 282 | /* TRANSLATORS: %s will be "revert" or "cherry-pick" */ |
| 283 | die(_("%s: Unable to write new index file"), action_name(opts)); |
| 284 | ``` |
| 285 | |
| 286 | We provide wrappers for C, Shell and Perl programs. Here's how they're |
| 287 | used: |
| 288 | |
| 289 | |
| 290 | ### C |
| 291 | |
| 292 | Include builtin.h at the top, it'll pull in gettext.h, which |
| 293 | defines the gettext interface. Consult with the list if you need to |
| 294 | use gettext.h directly. |
| 295 | |
| 296 | The C interface is a subset of the normal GNU gettext |
| 297 | interface. We currently export these functions: |
| 298 | |
| 299 | - \_() |
| 300 | |
| 301 | Mark and translate a string. E.g.: |
| 302 | |
| 303 | ```c |
| 304 | printf(_("HEAD is now at %s"), hex); |
| 305 | ``` |
| 306 | |
| 307 | - Q\_() |
| 308 | |
| 309 | Mark and translate a plural string. E.g.: |
| 310 | |
| 311 | ```c |
| 312 | printf(Q_("%d commit", "%d commits", number_of_commits)); |
| 313 | ``` |
| 314 | |
| 315 | This is just a wrapper for the ngettext() function. |
| 316 | |
| 317 | - N\_() |
| 318 | |
| 319 | A no-op pass-through macro for marking strings inside static |
| 320 | initializations, e.g.: |
| 321 | |
| 322 | ```c |
| 323 | static const char *reset_type_names[] = { |
| 324 | N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL |
| 325 | }; |
| 326 | ``` |
| 327 | |
| 328 | And then, later: |
| 329 | |
| 330 | ```c |
| 331 | die(_("%s reset is not allowed in a bare repository"), |
| 332 | _(reset_type_names[reset_type])); |
| 333 | ``` |
| 334 | |
| 335 | Here `_()` couldn't have statically determined what the translation |
| 336 | string will be, but since it was already marked for translation |
| 337 | with `N_()` the look-up in the message catalog will succeed. |
| 338 | |
| 339 | |
| 340 | ### Shell |
| 341 | |
| 342 | The Git gettext shell interface is just a wrapper for |
| 343 | gettext.sh. Import it right after git-sh-setup like this: |
| 344 | |
| 345 | ```shell |
| 346 | . git-sh-setup |
| 347 | . git-sh-i18n |
| 348 | ``` |
| 349 | |
| 350 | And then use the `gettext` or `eval_gettext` functions: |
| 351 | |
| 352 | ```shell |
| 353 | # For constant interface messages: |
| 354 | gettext "A message for the user"; echo |
| 355 | |
| 356 | # To interpolate variables: |
| 357 | details="oh noes" |
| 358 | eval_gettext "An error occurred: \$details"; echo |
| 359 | ``` |
| 360 | |
| 361 | In addition we have wrappers for messages that end with a trailing |
| 362 | newline. I.e. you could write the above as: |
| 363 | |
| 364 | ```shell |
| 365 | # For constant interface messages: |
| 366 | gettextln "A message for the user" |
| 367 | |
| 368 | # To interpolate variables: |
| 369 | details="oh noes" |
| 370 | eval_gettextln "An error occurred: \$details" |
| 371 | ``` |
| 372 | |
| 373 | More documentation about the interface is available in the GNU info |
| 374 | page: `info '(gettext)sh'`. Looking at git-am.sh (the first shell |
| 375 | command to be translated) for examples is also useful: |
| 376 | |
| 377 | ```shell |
| 378 | git log --reverse -p --grep=i18n git-am.sh |
| 379 | ``` |
| 380 | |
| 381 | |
| 382 | ### Perl |
| 383 | |
| 384 | The Git::I18N module provides a limited subset of the |
| 385 | Locale::Messages functionality, e.g.: |
| 386 | |
| 387 | ```perl |
| 388 | use Git::I18N; |
| 389 | print __("Welcome to Git!\n"); |
| 390 | printf __("The following error occurred: %s\n"), $error; |
| 391 | ``` |
| 392 | |
| 393 | Run `perldoc perl/Git/I18N.pm` for more info. |
| 394 | |
| 395 | |
| 396 | ## Testing marked strings |
| 397 | |
| 398 | Git's tests are run under `LANG=C LC_ALL=C`. So the tests do not need be |
| 399 | changed to account for translations as they're added. |
| 400 | |
| 401 | |
| 402 | ## AI-assisted translation and review |
| 403 | |
| 404 | [po/AGENTS.md](AGENTS.md) describes optional workflows for AI coding assistants |
| 405 | that help with Git localization: updating templates and PO files, translating |
| 406 | `po/XX.po`, and reviewing translations. Those workflows often use git-po-helper |
| 407 | together with the gettext tools; see the PO helper section below for what the |
| 408 | program does and how to build or install it. AI assistants are optional; treat |
| 409 | their output as a draft and have it reviewed by contributors who know Git and |
| 410 | the target language well. |
| 411 | |
| 412 | When you prompt a coding assistant, mention that file explicitly, for example: |
| 413 | "Translate po/XX.po with reference to po/AGENTS.md" (replace XX with your |
| 414 | language code). |
| 415 | |
| 416 | |
| 417 | ## PO helper |
| 418 | |
| 419 | `git-po-helper` is a helper for Git l10n coordinators and contributors. It |
| 420 | automates checks that contributions follow project conventions (PO syntax, |
| 421 | commit messages, which paths may change, and related rules) and can work with |
| 422 | AI coding agents for tasks such as translating new entries, and reviewing |
| 423 | translations. |
| 424 | |
| 425 | Build and install instructions are in [git-po-helper/README][]. |
| 426 | |
| 427 | |
| 428 | ## Conventions |
| 429 | |
| 430 | There are some conventions that l10n contributors must follow: |
| 431 | |
| 432 | - The subject of each l10n commit should be prefixed with "l10n: ". |
| 433 | |
| 434 | - Do not use non-ASCII characters in the subject of a commit. |
| 435 | |
| 436 | - The length of commit subject (first line of the commit log) should |
| 437 | be no more than 50 characters, and the length of other lines of the |
| 438 | commit log should be no more than 72 characters. |
| 439 | |
| 440 | - Add "Signed-off-by" trailer to your commit log, like other commits |
| 441 | in Git. You can automatically add the trailer by committing with |
| 442 | the following command: |
| 443 | |
| 444 | ```shell |
| 445 | git commit -s |
| 446 | ``` |
| 447 | |
| 448 | - Check syntax with "msgfmt" or the following command before creating |
| 449 | your commit: |
| 450 | |
| 451 | ```shell |
| 452 | git-po-helper check-po <XX.po> |
| 453 | ``` |
| 454 | |
| 455 | - Squash trivial commits to make history clear. |
| 456 | |
| 457 | - DO NOT edit files outside "po/" directory. |
| 458 | |
| 459 | - Other subsystems ("git-gui", "gitk", and Git itself) have their |
| 460 | own workflow. See [Documentation/SubmittingPatches][] for |
| 461 | instructions on how to contribute patches to these subsystems. |
| 462 | |
| 463 | |
| 464 | To contribute for a new l10n language, contributor should follow |
| 465 | additional conventions: |
| 466 | |
| 467 | - Initialize proper filename of the "XX.po" file conforming to |
| 468 | iso-639 and iso-3166. |
| 469 | |
| 470 | - Must complete a minimal translation based on the "Core |
| 471 | translation". See that section above. |
| 472 | |
| 473 | - Add a new entry in the "po/TEAMS" file with proper format, and check |
| 474 | the syntax of "po/TEAMS" by running the following command: |
| 475 | |
| 476 | ```shell |
| 477 | git-po-helper team --check |
| 478 | ``` |
| 479 | |
| 480 | |
| 481 | [git-po-helper/README]: https://github.com/git-l10n/git-po-helper#readme |
| 482 | [Documentation/SubmittingPatches]: Documentation/SubmittingPatches |