Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

purge windows carriage returns

grep -rl $'\r' * | xargs dos2unix
  • Loading branch information...
commit ab8bf9557864e44a5790a50080dae5e5620f3292 1 parent 28eca9a
@nospampleasemam nospampleasemam authored
View
14 docs/test.pod
@@ -1,7 +1,7 @@
-=head1 TEST
-
-=begin html
-
-<img src="https://raw.github.com/duckduckgo/duckduckgo/master/docs/ddg_request.png" alt="The big picture of DDG::Request" />
-
-=end html
+=head1 TEST
+
+=begin html
+
+<img src="https://raw.github.com/duckduckgo/duckduckgo/master/docs/ddg_request.png" alt="The big picture of DDG::Request" />
+
+=end html
View
34 lib/DDG/Block/Blockable/Any.pm
@@ -1,18 +1,18 @@
-package DDG::Block::Blockable::Any;
-# ABSTRACT: Role for something blockable that has no triggers
-
-use Moo::Role;
-
-with 'DDG::Block::Blockable';
-
-sub get_triggers {}
-sub triggers {}
-
-sub has_triggers { 0 }
-sub triggers_block_type { 'Any' }
-
-=head1 DESCRIPTION
-
-=cut
-
+package DDG::Block::Blockable::Any;
+# ABSTRACT: Role for something blockable that has no triggers
+
+use Moo::Role;
+
+with 'DDG::Block::Blockable';
+
+sub get_triggers {}
+sub triggers {}
+
+sub has_triggers { 0 }
+sub triggers_block_type { 'Any' }
+
+=head1 DESCRIPTION
+
+=cut
+
1;
View
74 lib/DDG/Language.pm
@@ -1,38 +1,38 @@
-package DDG::Language;
-# ABSTRACT: A language, can be empty [TODO]
-
-use Moo;
-
-my @language_attributes = qw(
- flagicon
- flag_url
- name_in_local
- rtl
- locale
- nplurals
- name_in_english
-);
-
- # 'en_US' => {
- # 'flagicon' => 'us',
- # 'flag_url' => 'https://duckduckgo.com/f2/us.png',
- # 'name_in_local' => 'English of United States',
- # -'translation_count' => 24,
- # -'percent' => '24',
- # 'rtl' => 0,
- # 'locale' => 'en_US',
- # 'nplurals' => 2,
- # 'name_in_english' => 'English of United States'
- # },
-
-has $_ => (
- is => 'ro',
- default => sub { '' }
-) for (@language_attributes);
-
-use overload '""' => sub {
- my $self = shift;
- return $self->locale;
-}, fallback => 1;
-
+package DDG::Language;
+# ABSTRACT: A language, can be empty [TODO]
+
+use Moo;
+
+my @language_attributes = qw(
+ flagicon
+ flag_url
+ name_in_local
+ rtl
+ locale
+ nplurals
+ name_in_english
+);
+
+ # 'en_US' => {
+ # 'flagicon' => 'us',
+ # 'flag_url' => 'https://duckduckgo.com/f2/us.png',
+ # 'name_in_local' => 'English of United States',
+ # -'translation_count' => 24,
+ # -'percent' => '24',
+ # 'rtl' => 0,
+ # 'locale' => 'en_US',
+ # 'nplurals' => 2,
+ # 'name_in_english' => 'English of United States'
+ # },
+
+has $_ => (
+ is => 'ro',
+ default => sub { '' }
+) for (@language_attributes);
+
+use overload '""' => sub {
+ my $self = shift;
+ return $self->locale;
+}, fallback => 1;
+
1;
View
80 lib/DDG/Location.pm
@@ -1,41 +1,41 @@
-package DDG::Location;
-# ABSTRACT: A location, can be empty (given by Geo::IP::Record)
-
-use Moo;
-
-my @geo_ip_record_attrs = qw( country_code country_code3 country_name region
- region_name city postal_code latitude longitude time_zone area_code
- continent_code metro_code );
-
-sub new_from_geo_ip_record {
- my ( $class, $geo_ip_record ) = @_;
- if ($geo_ip_record) {
- my %args;
- for (@geo_ip_record_attrs) {
- $args{$_} = $geo_ip_record->$_ if defined $geo_ip_record->$_;
- }
- return $class->new(
- geo_ip_record => $geo_ip_record,
- %args,
- );
- } else {
- return $class->new;
- }
-}
-
-has $_ => (
- is => 'ro',
- default => sub { '' }
-) for (@geo_ip_record_attrs);
-
-has geo_ip_record => (
- is => 'ro',
- predicate => 'has_geo_ip_record',
-);
-
-use overload '""' => sub {
- my $self = shift;
- return $self->country_code;
-}, fallback => 1;
-
+package DDG::Location;
+# ABSTRACT: A location, can be empty (given by Geo::IP::Record)
+
+use Moo;
+
+my @geo_ip_record_attrs = qw( country_code country_code3 country_name region
+ region_name city postal_code latitude longitude time_zone area_code
+ continent_code metro_code );
+
+sub new_from_geo_ip_record {
+ my ( $class, $geo_ip_record ) = @_;
+ if ($geo_ip_record) {
+ my %args;
+ for (@geo_ip_record_attrs) {
+ $args{$_} = $geo_ip_record->$_ if defined $geo_ip_record->$_;
+ }
+ return $class->new(
+ geo_ip_record => $geo_ip_record,
+ %args,
+ );
+ } else {
+ return $class->new;
+ }
+}
+
+has $_ => (
+ is => 'ro',
+ default => sub { '' }
+) for (@geo_ip_record_attrs);
+
+has geo_ip_record => (
+ is => 'ro',
+ predicate => 'has_geo_ip_record',
+);
+
+use overload '""' => sub {
+ my $self = shift;
+ return $self->country_code;
+}, fallback => 1;
+
1;
View
24 lib/DDG/Manual.pod
@@ -1,12 +1,12 @@
-# PODNAME: DDG::Manual
-
-=head1 NAME
-
-DDG::Manual - Overview of opensource documentations of DuckDuckGo
-
-=head1 GENERAL TOPICS
-
- * L<Overview of our translation system|DDG::Manual::Translation>
-
- * L<Overview of DuckPAN|DDG::Manual::DuckPAN> B<TODO>
-
+# PODNAME: DDG::Manual
+
+=head1 NAME
+
+DDG::Manual - Overview of opensource documentations of DuckDuckGo
+
+=head1 GENERAL TOPICS
+
+ * L<Overview of our translation system|DDG::Manual::Translation>
+
+ * L<Overview of DuckPAN|DDG::Manual::DuckPAN> B<TODO>
+
View
1,360 lib/DDG/Manual/Translation.pod
@@ -1,680 +1,680 @@
-# PODNAME: DDG::Manual::Translation
-
-=encoding utf-8
-
-=head1 NAME
-
-DDG::Manual::Translation - Overview of the translation system of DuckDuckGo
-
-=head1 THE MISSION
-
-Making the translation of a complex and grown system like DuckDuckGo is not an
-easy task. The system is scattered in very many subcomponents, which connect
-together over the user browser mostly. Many HTML snippets combined via code,
-coming from different system parts. Combined with many JavaScript and other
-microsolutions to solve specific components. On the other side, there is the
-pure problem of management and the translation itself. We are a small team,
-that limits our options, so we are required to coordinate the community
-together for making this all happen.
-
-=head2 ANALYZING THE MARKET
-
-We tried to use existing solutions for achieving the task, but felt not like
-that the solutions on the market are fulfilling our needs. Even when I (Getty)
-look back, I still think our biggest problems are coordination and defining
-proper processes for the translation flows, which are of course much easier to
-tune with an own system. The biggest lag I found in the systems we found at
-this point, was the lag of context and comments for the tokens. Also we want
-to introduce translation of pictures and other media, and also offer a special
-way for translating long texts, which was lagging on all platforms.
-
-=head1 WHAT IS TRANSLATION?
-
-Many people who never dived into the topic of translations, especially native
-english speaking persons, are not aware of the problems to face on
-translations. We would like to explain some of the base problems.
-
-=head2 Order of text
-
-You shouldn't think that the order of text is always the same in every
-language. You could easily compare this with the option in your own language
-to right a sentence in different ways, to imply or underline different topics
-of it. Those ways to promote specific parts are defined differently in many
-languages, also there are many things that are like very cultural specials
-that could hit the sentence. As a developer you might think you can do it like:
-
- 'You have ' + messages + ' messages'
-
-But this is not translatable. We will explain the solutions for this problem
-later.
-
-=head2 No direct translations possible
-
-Not every word can be 1:1 translated into another language. There are many
-cases, where specific context might change the meaning of a word, or you have
-lots of bigger diversity in the words, then you have in the language you use as
-reference. A common reference people make here, is the amount of
-L<Eskimo words for snow|http://en.wikipedia.org/wiki/Eskimo_words_for_snow>,
-which says that there are more than 50 different words for snow in the language
-eskimos are speaking. This is sadly a hoax, so we can't reference to it as
-example, but something you might stumble upon on airports in Germany is the
-sentence:
-
- Bitte anschnallen!
-
-Which is the english translation for:
-
- Fasten your seat belts!
-
-The german words, if you would translate them to english "directly", you would
-get the sentence:
-
- Please fasten!
-
-Noone speaking english would ever say it that way, you also think that misses
-out informations, but that is all right for germans, they understand exactly
-what you mean. A german would never say:
-
- Bitte festschnallen mit ihrem Sicherheitsgurt!
-
-which would be the "direct" translation of the english version. It would be
-just very very bad german to translate it that way, no german ever would do
-that. So the context of the words is very important sometimes, to get a real
-"human touch" in the translation you offer. Else we would use automatic
-translation systems, which are unable to find these kind of specialities in
-nearly all cases.
-
-Small subexample that directly comes up: Yes, B<anschnallen> and
-B<festschnallen> are actually the same word in english: B<fasten>. B<fasten>
-actually translates to L<12 different words in german|http://dict.leo.org/ende?searchLoc=-1&searchLocRelinked=-1&lp=ende&search=fasten&lp=ende&lang=de&searchLoc=0&searchLocRelinked=1&search=>.
-
-=head2 Right to left
-
-Yes, really, there are languages in the world, which are writing right to left.
-Beside the mess this brings to your hand if you are right handed, this also is
-a huge difference in the web interface. You can see how a right to left page
-looks like L<here|http://www.i18nguy.com/unicode/shma.html>. This also changes
-around most punctations and of course the flow of the page itself, even if you
-force a specific order, you must revert it for right to left.
-
-=head2 Plurality
-
-In most languages (like english), there are 2 purality cases: B<singular> and
-B<plural>. In those languages B<plural> is used, if you have none, or many.
-And B<singular> is only used, if you have just one:
-
- You have 1 message.
- You have 2 messages. (or also 0 or more than 2)
-
-In other languages, there are up to 5 different cases for B<plural>. Depending
-on sometimes complex math which we don't like to explain, but luckily the world
-has defined logic for this. This is a concept implemented in gettext, so this
-form is what we actually use, because our implementatin is on top of gettext
-for most base infrastructure. The english (and most other languages) plural
-definition for gettext is:
-
- nplurals=2; plural=(n != 1)
-
-This describes the logic, we mentioned above, that we have 2 "plural forms"
-(B<singular> and B<plural>), and the first plural form is used, if the amount
-described is not 1.
-
-Don't be scared! All those definitions are fixed, you can find them on this
-awesome page, and you dont need any more (beside if you want to define a
-fantasy language) L<http://translate.sourceforge.net/wiki/l10n/pluralforms>.
-
-In Slovak (the language spoken in L<Slovakia|https://duckduckgo.com/Slovakia>)
-there are 3 "plural forms", defined by this gettext definition:
-
- nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2
-
-So the text above would require 3 cases:
-
- Mas 1 spravu.
- Mas 2 spravy. (or 3 and 4)
- Mas 5 sprav. (or also 0 and more than 5)
-
-=head2 Gender cases
-
-Also relevant in most languages, is the gender, which might have influence to
-the case of the word. We just mention it in this documentation, as an element
-that could be taken into concern. So far our system is not able to handle this
-problem.
-
-=head1 TRANSLATION SYSTEM
-
-After understanding those base problems that come up, you might see, that it is
-very resource intensive to handle every problem of translations. We don't want
-to reinvent the wheel here, but the existing solutions are all not covering our
-needs, which means we need to make our own translation system, but trying to
-use as much existing solutions as possible, to reduce the amount of work.
-
-We use our own community platform for managing all the translations. This
-allows us to make very individual concepts and workflows specific for our
-needs. Especially integrating the translation with socializing components and
-more visualization options, is a key that allows us to give people who
-translate the texts have the optimum environment for understanding the deeper
-meaning of the text to translate.
-
-You should make an account at the L<community platform|https://dukgo.com/>, if
-you want to follow all steps of this documentation. This account is required
-for working with the translation system, but as long as you don't make your
-account public (you get an option for this in your account menu), noone will
-see any information about you, not even the username, only your translations
-will be stored in the database, so that the users can vote for it.
-
-The following sections explain all relevant components in detail.
-
-=head2 Storage
-
-I<This section is very technical, and can be skipped, if you are not interested
-in the technical decisions we made. Just go directly to L</Tokens>.>
-
-The storage for the translations, is a very important topic, it defines most of
-the decisions you have to make afterwards. The storage must be really fast and
-effective, especially inside the code. Replicating existing concepts here would
-produce a massive overhead, which leads to an analyze of the existing libraries
-for this topic which are fast enough for our requirements. Sadly, we also had
-to think about a solution that works inside JavaScript, so that we can
-integrate it most easy into our JavaScript code, which drives most of the
-visuals on a modern browser on DuckDuckGo.
-
-There are some pretty interesting solutions in Perl which allow us to really
-cover up all cases, like also gender, but those solutions are specific to Perl
-and can't work in JavaScript. In the end we decided "down" to the very common
-L<gettext|http://www.gnu.org/software/gettext/> system, which also has a
-L<JavaScript implementation|http://jsgettext.berlios.de/> and is covered with
-implementations in all languages, so Perl, Ruby, Python and other languages
-where we might need to integrate translation.
-
-Especially the existence of a very wide used and accepted plain C
-implementation makes it also reliable in many ways. The C library delivers
-directly a commandline tool to convert text based datafiles for the
-translations (the so called po files), to high effective binary files to make
-this data accessable very fast (the binary file is called mo). This tool is
-called B<msgfmt> and included in the B<gettext> package of your distribution.
-
-In the JavaScript implementation we have a small Perl program B<po2json> which
-converts the same text datafile into a json that is better usable in
-JavaScript. Sadly this datafile must be of course loaded for the browser, you
-might see that big JavaScript file on the load of DuckDuckGo which integrates
-the translations together with the libraries for using those. We compress this
-to make it smaller for the bandwidth. More optimization options are open here.
-
-In the end gettext is able to solve the problem about the non direct
-translations and the plurality cases. It is by itself not able to solve the
-gender case or the order of text problem, especially in combination with
-combined elements that have to be translated independent. The option to extend
-our system todo also the gender case is open, but we are not heading towards
-this yet.
-
-=head2 Locale::Simple
-
-Many people who never did translation before, but heard of I<gettext>, think
-that I<gettext> itself directly handles everything you need for the
-translation, but it has no real concept for combined tokens or placeholders
-for dynamic text. I<gettext> works only as storage and accessor for the
-translations you need. It solves lots of problems that you really can't solve
-easily alone, but it misses those very important details.
-
-We need still to wrap I<gettext> with L<sprintf|https://duckduckgo.com/sprintf>
-to make it really useful. This will allow us to combine tokens with HTML and
-other formattings. We will describe this in the next section.
-
-We released this wrapping, which makes the exactly same API for Perl, Python and
-JavaScript on L<CPAN|http://cpan.org/> and L<pypi|http://pypi.python.org/>. You
-can install it with cpan or your prefered CPAN package installer for Perl, like
-with L<App::cpanminus|https://metacpan.org/module/App::cpanminus>:
-
- cpanm Locale::Simple
-
-or for python with pip2 in your userspace:
-
- pip2 install --user locale-simple
-
-Inside the Perl distribution, you find also all the JavaScript required, if you
-want to use it for your own project someday. If you want to play around with
-Perl in general, please consider installing L<perlbrew|http://www.perlbrew.pl/>
-first. It's not required to install any of this to contribute.
-
-=head2 Tokens
-
-The storage also determines the way we define the base of our workflow about
-the translations. These are the so called B<tokens>, which is the main part of
-all the flow about translation. The coders are making tokens in the code, the
-templates or wherever its needed. Those tokens define the texts that have to
-get translated. So, a very important point here, is to understand, that the
-text that has to be translated is the token, lets see some examples of
-templates that makes it easier to understand.
-
-=head3 Simple token
-
- <: l('Monthly newsletter:') :>
-
-So this defines a simple token with the text 'Monthy newsletter:'. So
-I<gettext>, our translation storage, actually has no data file for these so
-called tokens itself, cause the text data file only contains the token AND a
-translation. But for having a good normalization we store this in our database
-under the same fieldnames that I<gettext> wants, so i display it to you now
-like a "partly" I<po> file without the translation, this makes it easier to
-understand it:
-
- msgid "Monthly newsletter:"
-
-Those tokens we store in the database of our community platform at
-L<https://dukgo.com/translate/do/index>. For the token itself exist no page,
-but here is the page for this specific token in german:
-L<https://dukgo.com/translate/tokenlanguage/26811>.
-
-In the general translation interface of the community platform, you normally
-see a list of those tokens, but we will explain the translation interface
-later, to not make it to complex for now, but you see the text to translate
-right to the word "Singular" on top. Below you see the translations of other
-users for it, there is (right now) only one for german.
-
-When there is no translation found, the system always gives back the token
-itself. At DuckDuckGo all tokens are given in English of the United States.
-
-=head3 Token with context
-
-As you now see, this is a very simple token, it is just text, it is not like
-really touching any of our problem cases. Another problem case, would be for
-example the token I<Medium>. This is a very very vague word, you need a bit of
-a context to really find the right translation, even if you think in english
-it is very clear, you can imagine that a lonely I<Medium> can be in lots of
-different kind of context. I<gettext> offers here the option to give a so called
-B<context> additional to a token, which allows us, to give a bit more "context"
-without changing the token itself. In the template it would look like this:
-
- <: lp('size','Medium') :>
-
-and in the I<gettext> storage:
-
- msgctxt "size"
- msgid "Medium"
-
-This means that we want the token "Medium" in the context of "size". The
-advantage here is now, if there would be for example:
-
- <: lp('weight','Medium') :>
-
-Then there are 2 tokens in the system to translate, both with different
-context. Here you can see the page for the german translation of this shown
-token L<https://dukgo.com/translate/tokenlanguage/26671>. As you see, above
-the word that has to be translate, you see B<Context>, which SHOULD be not
-taken as really description for the context, instead this context helps all
-people working with the tokens to find this specific token in the code,
-templates or wherever it needs to be coordinated, and it should be a much
-clearer description in the notes for this token.
-
-So here is already a very first thing to take care about, if you are
-responsible for working with tokens, you can't give everything a context, else
-the reusage of tokens is much harder, but you also can't expect that everything
-works fine without using any context for specific words, especially if they are
-very lonely.
-
-=head3 Placeholders in tokens
-
-Placeholders in tokens are giving many options to make the displaying of the
-text more finetuned. Often it is required that inside the text itself you need
-a special wrapping for the display, like HTML, this can be achieved with
-placeholders. They are also used to allow number specific case decision, the
-problem described L</Plurality> section. Here a simple example of a dynamic
-text inside a token:
-
- <: l("Hello %s!",$username) :>
-
-This allows to give a username towards the token, but still allows the
-translator to move the dynamic text to another place, which is more proper in
-his language (like if it would be right to left, the placeholder might be more
-left). 2 things are happening now in the translation system:
-
-At first I<gettext> will try to find the translation for this token given,
-and in the translation the translator also keeps this placeholder B<%s>. After
-this translation is found, for example going B<Pirate>:
-
- %s, ahoi! hrrr
-
-Given this example translation, the translated text for a user switched to
-L<Pirate|https://duckduckgo.com/?kad=pirate_XX>, with the username B<yegg>,
-would be:
-
- yegg, ahoi! hrrr
-
-=head3 Placeholders and plurality
-
-Additional to placeholders for text, we always cover combined with I<gettext>
-the option for dynamic numbered cases, which requires to decide for the proper
-plurality case in the language, and replace the placeholder for the number with
-the number given for the case. Here an example for a numbered case of a token
-in the template (or code, or JavaScript):
-
- <: ln("You have %d message.","You have %d messages.",$messages) :>
-
-This is for defining a token which is based on the number for the specific
-token. In the definition in the I<gettext> storage it ends like this:
-
- msgid "You have %d message."
- msgid_plural "You have %d messages."
-
-Just to directly show, this can, of course, be combined with a B<context>:
-
- <: lnp("email","You have %d message.","You have %d messages.",$messages) :>
-
-which ends up in this form in I<gettext>:
-
- msgctxt "email"
- msgid "You have %d message."
- msgid_plural "You have %d messages."
-
-First, I<gettext> will check with the current plural definitions (see above),
-what specific plural case is required for this translation. As mentioned on the
-section L</Plurality>, some languages might have more then 2 forms for
-this. But whatever it is, I<gettext> handles this with the translation datafile
-for the given language. I will show a translated example later.
-
-After I<gettext> has picked the correct translated text, it will put this
-translation towards L<sprintf|https://duckduckgo.com/sprintf>, which replaces
-the placeholders with the proper values.
-
-If we have B<$messages> like B<3> on the above example, the output would be:
-
- You have 3 messages.
-
-The combination of I<gettext> and I<sprintf> here sadly has the disadvantage,
-to force the count that defines the plural form to be the first placeholder.
-This makes problems in the combination with combined tokens. We explain the
-problem in the section L</Combined tokens>, later in this manual.
-
-The following section about sprintf describes more deeply how the placeholders
-are functioning, but normally this is only relevant for developers who
-generate tokens for the system.
-
-=head4 sprintf
-
-I<This section is very technical, and can be skipped. Just go directly to
-L</Combined tokens>.>
-
-sprintf is a function of C that defines the so called printf conventions for
-formatting a text with dynamic data. You will find it in every language, so
-you can always reference to the usage via your favorite programming language
-documentation. A little of the most relevant overview I will describe here:
-
-sprintf is made to take a format definition and some values for the dynamic
-parts in this definition to produce a new string. A simple example in Perl
-would be:
-
- perl -e'print sprintf("%s is my search engine!","DuckDuckGo")."\n";'
-
-The output of this will be:
-
- DuckDuckGo is my search engine!
-
-The B<%s> in the first parameter of sprintf, is replaced with the second
-parameter we have given to the sprintf call. This is a very simple example,
-B<%s> means B<string> in this case. Alternative options are:
-
- %% a percent sign
- %c a character with the given number
- %s a string
- %d a signed integer, in decimal
- %u an unsigned integer, in decimal
- %o an unsigned integer, in octal
- %x an unsigned integer, in hexadecimal
- %e a floating-point number, in scientific notation
- %f a floating-point number, in fixed decimal notation
- %g a floating-point number, in %e or %f notation
-
-You normally will only face B<%s> and probably B<%d> in the context of
-DuckDuckGo, so don't be scared about the other options.
-
-B<%d> is specific to display a number, as decimal, so if you would say:
-
- sprintf("%d bottles of beer",99.0);
-
-In your language, where 99.0 defines a float number, you would still get back:
-
- "99 bottles of beer"
-
-A very important point here is the option to give several parameters, AND
-reorder them in the usage, for example:
-
- sprintf("From %s to %s",'A','B');
- # returns "From A to B"
-
-This seems to force always B<$from> in the first %s that appears, and B<$to> in
-the second appreance of %s. If in some language for example the order for this
-case gets "other around", then we can't change the order in the code of course,
-or make a switch. Luckily sprintf allows us to use the data in other order then
-given, as you can see on this example:
-
- sprintf("To %2$s from %1$s",'A','B');
- # returns "To B from A"
-
-This tells sprintf to put the first extra value into the B<%1$s> and the second
-extra value into B<%2$s>. So, if a translation that hits several placeholders,
-needs a different order of them, it can use this syntax to achieve this.
-
-If you want to know more about sprintf, you can checkout the
-L<perldoc to sprintf|http://perldoc.perl.org/functions/sprintf.html>, but for
-anything related to tokens, you leave the placeholders always as is in the
-tokens. Deeper knowledge of sprintf is only required for people who define
-tokens.
-
-=head3 Combined tokens
-
-With the understandment of sprintf, we are now able to make tokens specific for
-special visual needs, like if they need additional HTML. An example could be:
-
- <: l('%s for more info!', '<a href="...">' ~ l('Click here') ~ '</a>' :>
-
-Would give out:
-
- <a href="...">Click here</a> for more info!
-
-Which allows us to exclude the HTML from the translation, and still give the
-translator enough freedom to define which part of his text is the text that
-should be made clickable (or colored differently or whatever).
-
-A bigger problem exist, if we combine those placeholders in combination with
-number placeholders, explained in the section L</Placeholders and plurality>.
-The concept forces to get the count that is used to determine the proper plural
-form must be set to the first position. Which could lead to a problem, like in
-this case:
-
- $username has $count message.
- $username has $count messages.
-
-If we would try to convert this to B<"%s has %d messages.">, we would run into
-the problem, that the first placeholder would be B<$username> and not
-B<$count>. To avoid this we can use the method of I<sprintf> to change the
-order of the values given for the placeholders, the right solution would be:
-
- <: ln("%2$s has %1$d message.","%2$s has %1$d messages.",$count,$username) :>
-
-The very big disadvantage here is the organizational part. It is really complex
-to have all those tokens in the database and still reference which ones are
-staying together. It always requires lots of comments and further information.
-In some very awkward cases you a real extreme cascading of the tokens. In those
-cases it is really essential to generated B<context>, here a bigger example
-from our JavaScript:
-
- lp('webelieve','%s believe in %s AND %s.',
- '<a href="/about">' + lp('webelieve','We') + '</a>',
- '<a href="/goodies">' + lp('webelieve','better search') + '</a>',
- '<a href="http://donttrack.us">' + lp('webelieve','no tracking') + '</a>'
- );
-
-Which is in gettext written this:
-
- msgctxt "webelieve"
- msgid "%s believe in %s AND %s."
-
- msgctxt "webelieve"
- msgid "We"
-
- msgctxt "webelieve"
- msgid "better search"
-
- msgctxt "webelieve"
- msgid "no tracking"
-
-You can now imagine, that without a bit more comment, it is very hard to get
-it right to translate those texts. Best is if the user additional has an URL
-to see the tokens in action. Especially in the flow of all untranslated tokens,
-which is what most users do to help us translate.
-
-Most combined tokens are gathered under one specific B<msgctxt>, in the
-translation interface, you can click on the context given in the interface to
-reach a page with all tokens of this specific context. Still we try to add
-comments to every token that describe the complete text context where the token
-is used.
-
-=head3 Token translation functions
-
- l( msgid, ... )
- ln( msgid, msgid_plural, count, ... )
- lp( msgctxt, msgid, ... )
- lnp( msgctxt, msgid, msgid_plural, count, ... )
-
-=head3 Token translation storage
-
-As described in the previous sections, the database of the community platform
-stores all the translations, which then gets generated to the I<po> files used
-by I<gettext> in our translation system. Here I show you the german
-translations of the examples from above from the I<po> that gets generated:
-
- msgid "Monthly newsletter:"
- msgstr "Monatlicher Newsletter:"
-
- msgctxt "size"
- msgid "Medium"
- msgstr "Mittelgroß"
-
- msgid "Hello %s!"
- msgstr "Hallo %s!"
-
- msgctxt "email"
- msgid "You have %d message."
- msgid_plural "You have %d messages."
- msgstr[0] "Du hast %d Nachricht."
- msgstr[1] "Du hast %d Nachrichten."
-
- msgid "%2$s has %1$d message."
- msgid_plural "%2$s has %1$d messages."
- msgstr[0] "%2$s hat %1$d Nachricht."
- msgstr[1] "%2$s hat %1$d Nachrichten."
-
- msgid "From %s to %s"
- msgstr "Von %s nach %s"
-
-The pirate translation of the B<"Hello %s!"> example would look like this:
-
- msgid "Hello %s!"
- msgstr "%s, ahoi! hrrr"
-
-Our example to change the order of the placeholders would look like this:
-
- msgid "From %s to %s"
- msgstr "To %2$s from %1$s"
-
-In the case of a language which has more than 2 plural forms (See L</Plurality>), the
-number in the brackets will just get stacked up:
-
- msgid "You have %d message."
- msgid_plural "You have %d messages."
- msgstr[0] "Mas %d spravu."
- msgstr[1] "Mas %d spravy."
- msgstr[2] "Mas %d sprav."
-
-Interesting sidenote, the highest amount of plural forms is 6. You can find this in
-the L<arabic language|https://duckduckgo.com/?q=arabic>.
-
-In the case of the placeholder with count and a dynamic text, a translation
-which uses the order correct, can of course then use the normal I<sprintf>
-placeholders definition:
-
- msgid "%2$s has %1$d message."
- msgid_plural "%2$s has %1$d messages."
- msgstr[0] "%d Nachricht hat %s."
- msgstr[1] "%d Nachrichten hat %s."
-
-=head3 Voting translations
-
-On the community platform, you are able to vote for an existing translation,
-instead of making your own translation. If you, by mistake, not saw the
-existing translation, your translation will automatically get converted to a
-note for this translation.
-
-=head3 Used translation
-
-The system which generates the translation I<po> files for all the languages,
-picks the translation by finding the translation with the most votes. If there
-are several translations with the same amount of votes, the translation will be
-used, where the translator has the highest B<grade> in this language. We will
-explain this in the community platform section (L</The community platform>)
-more deeply. This process happens on the release of the translations.
-
-=head3 Releasing translations
-
-I<This section is very technical, and can be skipped. Just go directly to
-L</Token domains>.>
-
-The generated I<po> files will be packed together with all other necessary data
-files, like the I<mo> file and the I<json> file for the JavaScript, as a Perl
-distribution for our central B<DuckPAN> server, which is used to fetch the
-releases of the open source development to our live systems. The code for this
-procedures is in the package L<DDGC::LocaleDist|https://github.com/duckduckgo/community-platform/blob/master/lib/DDGC/LocaleDist.pm>
-in the L<source code of the community platform|https://github.com/duckduckgo/community-platform>.
-
-=head2 Token domains
-
-For organizational reasons, but also for technical reasons, we are required to
-group the tokens in so called B<token domains>. The terminology is taken from
-I<gettext>, which also defines one I<po> file is one specific domain. We
-normally define a default domain on initalization of our translation library.
-This way we never need to care about the domain on the function calls for the
-translation library.
-
-Also the release of the translations (See L<|/Releasing translations>) is
-executed for every token domain individually. On the live systems we install
-all the latest releases of all token domains at once.
-
-=head3 Token domains in the community platform
-
-In the community platform (L</The community platform>) the different token
-domains are an independent list of tokens. This allows translators to pick the
-block of tokens, they want to work on. It is very important to understand that
-a half done translation is nearly as usable as no translation at all. The
-translators must be encouraged to finish up all tokens of a domain, only then
-it really makes sense to promote wider the specific project part to the users.
-We explain more about this in the section L</The community platform>.
-
-=head3 Token domain translation functions
-
-I<This section is very technical, and can be skipped. Just go directly to
-L</Overlapping tokens>.>
-
-Our translation library (See L<|/Locale::Simple>) also supports functions which
-use a specific token domain directly as parameter, but those functions are (so
-far) never used at DuckDuckGo. A switch between several domains inside one code
-file or template is avoided. They are called B<ld()>, B<ldn()>, B<ldp()> and
-B<ldnp()> and work exactly like already described translation functions, but
-take the name for the token domain that has to be used as first parameter. The
-function to set the default token domain is B<ltd()>.
-
-=head3 Overlapping tokens
-
-The definition of a token is bound to its token domain. This means, that 2
-identical tokens in 2 domains are still 2 independent tokens and still both
-need to be translated. In most cases, this sadly leads to a pointless
-translation of the exactly same meaning and probably even in the exactly same
-context. It is very hard to avoid this.
-
-Still there are cases left where the context given with the token domain might
-slighty fix the interpretation more deeply. Only because a token seems very
-identical in the english language does not directly lead to the same
-interpretation with other languages.
-
-=head2 The community platform
-
+# PODNAME: DDG::Manual::Translation
+
+=encoding utf-8
+
+=head1 NAME
+
+DDG::Manual::Translation - Overview of the translation system of DuckDuckGo
+
+=head1 THE MISSION
+
+Making the translation of a complex and grown system like DuckDuckGo is not an
+easy task. The system is scattered in very many subcomponents, which connect
+together over the user browser mostly. Many HTML snippets combined via code,
+coming from different system parts. Combined with many JavaScript and other
+microsolutions to solve specific components. On the other side, there is the
+pure problem of management and the translation itself. We are a small team,
+that limits our options, so we are required to coordinate the community
+together for making this all happen.
+
+=head2 ANALYZING THE MARKET
+
+We tried to use existing solutions for achieving the task, but felt not like
+that the solutions on the market are fulfilling our needs. Even when I (Getty)
+look back, I still think our biggest problems are coordination and defining
+proper processes for the translation flows, which are of course much easier to
+tune with an own system. The biggest lag I found in the systems we found at
+this point, was the lag of context and comments for the tokens. Also we want
+to introduce translation of pictures and other media, and also offer a special
+way for translating long texts, which was lagging on all platforms.
+
+=head1 WHAT IS TRANSLATION?
+
+Many people who never dived into the topic of translations, especially native
+english speaking persons, are not aware of the problems to face on
+translations. We would like to explain some of the base problems.
+
+=head2 Order of text
+
+You shouldn't think that the order of text is always the same in every
+language. You could easily compare this with the option in your own language
+to right a sentence in different ways, to imply or underline different topics
+of it. Those ways to promote specific parts are defined differently in many
+languages, also there are many things that are like very cultural specials
+that could hit the sentence. As a developer you might think you can do it like:
+
+ 'You have ' + messages + ' messages'
+
+But this is not translatable. We will explain the solutions for this problem
+later.
+
+=head2 No direct translations possible
+
+Not every word can be 1:1 translated into another language. There are many
+cases, where specific context might change the meaning of a word, or you have
+lots of bigger diversity in the words, then you have in the language you use as
+reference. A common reference people make here, is the amount of
+L<Eskimo words for snow|http://en.wikipedia.org/wiki/Eskimo_words_for_snow>,
+which says that there are more than 50 different words for snow in the language
+eskimos are speaking. This is sadly a hoax, so we can't reference to it as
+example, but something you might stumble upon on airports in Germany is the
+sentence:
+
+ Bitte anschnallen!
+
+Which is the english translation for:
+
+ Fasten your seat belts!
+
+The german words, if you would translate them to english "directly", you would
+get the sentence:
+
+ Please fasten!
+
+Noone speaking english would ever say it that way, you also think that misses
+out informations, but that is all right for germans, they understand exactly
+what you mean. A german would never say:
+
+ Bitte festschnallen mit ihrem Sicherheitsgurt!
+
+which would be the "direct" translation of the english version. It would be
+just very very bad german to translate it that way, no german ever would do
+that. So the context of the words is very important sometimes, to get a real
+"human touch" in the translation you offer. Else we would use automatic
+translation systems, which are unable to find these kind of specialities in
+nearly all cases.
+
+Small subexample that directly comes up: Yes, B<anschnallen> and
+B<festschnallen> are actually the same word in english: B<fasten>. B<fasten>
+actually translates to L<12 different words in german|http://dict.leo.org/ende?searchLoc=-1&searchLocRelinked=-1&lp=ende&search=fasten&lp=ende&lang=de&searchLoc=0&searchLocRelinked=1&search=>.
+
+=head2 Right to left
+
+Yes, really, there are languages in the world, which are writing right to left.
+Beside the mess this brings to your hand if you are right handed, this also is
+a huge difference in the web interface. You can see how a right to left page
+looks like L<here|http://www.i18nguy.com/unicode/shma.html>. This also changes
+around most punctations and of course the flow of the page itself, even if you
+force a specific order, you must revert it for right to left.
+
+=head2 Plurality
+
+In most languages (like english), there are 2 purality cases: B<singular> and
+B<plural>. In those languages B<plural> is used, if you have none, or many.
+And B<singular> is only used, if you have just one:
+
+ You have 1 message.
+ You have 2 messages. (or also 0 or more than 2)
+
+In other languages, there are up to 5 different cases for B<plural>. Depending
+on sometimes complex math which we don't like to explain, but luckily the world
+has defined logic for this. This is a concept implemented in gettext, so this
+form is what we actually use, because our implementatin is on top of gettext
+for most base infrastructure. The english (and most other languages) plural
+definition for gettext is:
+
+ nplurals=2; plural=(n != 1)
+
+This describes the logic, we mentioned above, that we have 2 "plural forms"
+(B<singular> and B<plural>), and the first plural form is used, if the amount
+described is not 1.
+
+Don't be scared! All those definitions are fixed, you can find them on this
+awesome page, and you dont need any more (beside if you want to define a
+fantasy language) L<http://translate.sourceforge.net/wiki/l10n/pluralforms>.
+
+In Slovak (the language spoken in L<Slovakia|https://duckduckgo.com/Slovakia>)
+there are 3 "plural forms", defined by this gettext definition:
+
+ nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2
+
+So the text above would require 3 cases:
+
+ Mas 1 spravu.
+ Mas 2 spravy. (or 3 and 4)
+ Mas 5 sprav. (or also 0 and more than 5)
+
+=head2 Gender cases
+
+Also relevant in most languages, is the gender, which might have influence to
+the case of the word. We just mention it in this documentation, as an element
+that could be taken into concern. So far our system is not able to handle this
+problem.
+
+=head1 TRANSLATION SYSTEM
+
+After understanding those base problems that come up, you might see, that it is
+very resource intensive to handle every problem of translations. We don't want
+to reinvent the wheel here, but the existing solutions are all not covering our
+needs, which means we need to make our own translation system, but trying to
+use as much existing solutions as possible, to reduce the amount of work.
+
+We use our own community platform for managing all the translations. This
+allows us to make very individual concepts and workflows specific for our
+needs. Especially integrating the translation with socializing components and
+more visualization options, is a key that allows us to give people who
+translate the texts have the optimum environment for understanding the deeper
+meaning of the text to translate.
+
+You should make an account at the L<community platform|https://dukgo.com/>, if
+you want to follow all steps of this documentation. This account is required
+for working with the translation system, but as long as you don't make your
+account public (you get an option for this in your account menu), noone will
+see any information about you, not even the username, only your translations
+will be stored in the database, so that the users can vote for it.
+
+The following sections explain all relevant components in detail.
+
+=head2 Storage
+
+I<This section is very technical, and can be skipped, if you are not interested
+in the technical decisions we made. Just go directly to L</Tokens>.>
+
+The storage for the translations, is a very important topic, it defines most of
+the decisions you have to make afterwards. The storage must be really fast and
+effective, especially inside the code. Replicating existing concepts here would
+produce a massive overhead, which leads to an analyze of the existing libraries
+for this topic which are fast enough for our requirements. Sadly, we also had
+to think about a solution that works inside JavaScript, so that we can
+integrate it most easy into our JavaScript code, which drives most of the
+visuals on a modern browser on DuckDuckGo.
+
+There are some pretty interesting solutions in Perl which allow us to really
+cover up all cases, like also gender, but those solutions are specific to Perl
+and can't work in JavaScript. In the end we decided "down" to the very common
+L<gettext|http://www.gnu.org/software/gettext/> system, which also has a
+L<JavaScript implementation|http://jsgettext.berlios.de/> and is covered with
+implementations in all languages, so Perl, Ruby, Python and other languages
+where we might need to integrate translation.
+
+Especially the existence of a very wide used and accepted plain C
+implementation makes it also reliable in many ways. The C library delivers
+directly a commandline tool to convert text based datafiles for the
+translations (the so called po files), to high effective binary files to make
+this data accessable very fast (the binary file is called mo). This tool is
+called B<msgfmt> and included in the B<gettext> package of your distribution.
+
+In the JavaScript implementation we have a small Perl program B<po2json> which
+converts the same text datafile into a json that is better usable in
+JavaScript. Sadly this datafile must be of course loaded for the browser, you
+might see that big JavaScript file on the load of DuckDuckGo which integrates
+the translations together with the libraries for using those. We compress this
+to make it smaller for the bandwidth. More optimization options are open here.
+
+In the end gettext is able to solve the problem about the non direct
+translations and the plurality cases. It is by itself not able to solve the
+gender case or the order of text problem, especially in combination with
+combined elements that have to be translated independent. The option to extend
+our system todo also the gender case is open, but we are not heading towards
+this yet.
+
+=head2 Locale::Simple
+
+Many people who never did translation before, but heard of I<gettext>, think
+that I<gettext> itself directly handles everything you need for the
+translation, but it has no real concept for combined tokens or placeholders
+for dynamic text. I<gettext> works only as storage and accessor for the
+translations you need. It solves lots of problems that you really can't solve
+easily alone, but it misses those very important details.
+
+We need still to wrap I<gettext> with L<sprintf|https://duckduckgo.com/sprintf>
+to make it really useful. This will allow us to combine tokens with HTML and
+other formattings. We will describe this in the next section.
+
+We released this wrapping, which makes the exactly same API for Perl, Python and
+JavaScript on L<CPAN|http://cpan.org/> and L<pypi|http://pypi.python.org/>. You
+can install it with cpan or your prefered CPAN package installer for Perl, like
+with L<App::cpanminus|https://metacpan.org/module/App::cpanminus>:
+
+ cpanm Locale::Simple
+
+or for python with pip2 in your userspace:
+
+ pip2 install --user locale-simple
+
+Inside the Perl distribution, you find also all the JavaScript required, if you
+want to use it for your own project someday. If you want to play around with
+Perl in general, please consider installing L<perlbrew|http://www.perlbrew.pl/>
+first. It's not required to install any of this to contribute.
+
+=head2 Tokens
+
+The storage also determines the way we define the base of our workflow about
+the translations. These are the so called B<tokens>, which is the main part of
+all the flow about translation. The coders are making tokens in the code, the
+templates or wherever its needed. Those tokens define the texts that have to
+get translated. So, a very important point here, is to understand, that the
+text that has to be translated is the token, lets see some examples of
+templates that makes it easier to understand.
+
+=head3 Simple token
+
+ <: l('Monthly newsletter:') :>
+
+So this defines a simple token with the text 'Monthy newsletter:'. So
+I<gettext>, our translation storage, actually has no data file for these so
+called tokens itself, cause the text data file only contains the token AND a
+translation. But for having a good normalization we store this in our database
+under the same fieldnames that I<gettext> wants, so i display it to you now
+like a "partly" I<po> file without the translation, this makes it easier to
+understand it:
+
+ msgid "Monthly newsletter:"
+
+Those tokens we store in the database of our community platform at
+L<https://dukgo.com/translate/do/index>. For the token itself exist no page,
+but here is the page for this specific token in german:
+L<https://dukgo.com/translate/tokenlanguage/26811>.
+
+In the general translation interface of the community platform, you normally
+see a list of those tokens, but we will explain the translation interface
+later, to not make it to complex for now, but you see the text to translate
+right to the word "Singular" on top. Below you see the translations of other
+users for it, there is (right now) only one for german.
+
+When there is no translation found, the system always gives back the token
+itself. At DuckDuckGo all tokens are given in English of the United States.
+
+=head3 Token with context
+
+As you now see, this is a very simple token, it is just text, it is not like
+really touching any of our problem cases. Another problem case, would be for
+example the token I<Medium>. This is a very very vague word, you need a bit of
+a context to really find the right translation, even if you think in english
+it is very clear, you can imagine that a lonely I<Medium> can be in lots of
+different kind of context. I<gettext> offers here the option to give a so called
+B<context> additional to a token, which allows us, to give a bit more "context"
+without changing the token itself. In the template it would look like this:
+
+ <: lp('size','Medium') :>
+
+and in the I<gettext> storage:
+
+ msgctxt "size"
+ msgid "Medium"
+
+This means that we want the token "Medium" in the context of "size". The
+advantage here is now, if there would be for example:
+
+ <: lp('weight','Medium') :>
+
+Then there are 2 tokens in the system to translate, both with different
+context. Here you can see the page for the german translation of this shown
+token L<https://dukgo.com/translate/tokenlanguage/26671>. As you see, above
+the word that has to be translate, you see B<Context>, which SHOULD be not
+taken as really description for the context, instead this context helps all
+people working with the tokens to find this specific token in the code,
+templates or wherever it needs to be coordinated, and it should be a much
+clearer description in the notes for this token.
+
+So here is already a very first thing to take care about, if you are
+responsible for working with tokens, you can't give everything a context, else
+the reusage of tokens is much harder, but you also can't expect that everything
+works fine without using any context for specific words, especially if they are
+very lonely.
+
+=head3 Placeholders in tokens
+
+Placeholders in tokens are giving many options to make the displaying of the
+text more finetuned. Often it is required that inside the text itself you need
+a special wrapping for the display, like HTML, this can be achieved with
+placeholders. They are also used to allow number specific case decision, the
+problem described L</Plurality> section. Here a simple example of a dynamic
+text inside a token:
+
+ <: l("Hello %s!",$username) :>
+
+This allows to give a username towards the token, but still allows the
+translator to move the dynamic text to another place, which is more proper in
+his language (like if it would be right to left, the placeholder might be more
+left). 2 things are happening now in the translation system:
+
+At first I<gettext> will try to find the translation for this token given,
+and in the translation the translator also keeps this placeholder B<%s>. After
+this translation is found, for example going B<Pirate>:
+
+ %s, ahoi! hrrr
+
+Given this example translation, the translated text for a user switched to
+L<Pirate|https://duckduckgo.com/?kad=pirate_XX>, with the username B<yegg>,
+would be:
+
+ yegg, ahoi! hrrr
+
+=head3 Placeholders and plurality
+
+Additional to placeholders for text, we always cover combined with I<gettext>
+the option for dynamic numbered cases, which requires to decide for the proper
+plurality case in the language, and replace the placeholder for the number with
+the number given for the case. Here an example for a numbered case of a token
+in the template (or code, or JavaScript):
+
+ <: ln("You have %d message.","You have %d messages.",$messages) :>
+
+This is for defining a token which is based on the number for the specific
+token. In the definition in the I<gettext> storage it ends like this:
+
+ msgid "You have %d message."
+ msgid_plural "You have %d messages."
+
+Just to directly show, this can, of course, be combined with a B<context>:
+
+ <: lnp("email","You have %d message.","You have %d messages.",$messages) :>
+
+which ends up in this form in I<gettext>:
+
+ msgctxt "email"
+ msgid "You have %d message."
+ msgid_plural "You have %d messages."
+
+First, I<gettext> will check with the current plural definitions (see above),
+what specific plural case is required for this translation. As mentioned on the
+section L</Plurality>, some languages might have more then 2 forms for
+this. But whatever it is, I<gettext> handles this with the translation datafile
+for the given language. I will show a translated example later.
+
+After I<gettext> has picked the correct translated text, it will put this
+translation towards L<sprintf|https://duckduckgo.com/sprintf>, which replaces
+the placeholders with the proper values.
+
+If we have B<$messages> like B<3> on the above example, the output would be:
+
+ You have 3 messages.
+
+The combination of I<gettext> and I<sprintf> here sadly has the disadvantage,
+to force the count that defines the plural form to be the first placeholder.
+This makes problems in the combination with combined tokens. We explain the
+problem in the section L</Combined tokens>, later in this manual.
+
+The following section about sprintf describes more deeply how the placeholders
+are functioning, but normally this is only relevant for developers who
+generate tokens for the system.
+
+=head4 sprintf
+
+I<This section is very technical, and can be skipped. Just go directly to
+L</Combined tokens>.>
+
+sprintf is a function of C that defines the so called printf conventions for
+formatting a text with dynamic data. You will find it in every language, so
+you can always reference to the usage via your favorite programming language
+documentation. A little of the most relevant overview I will describe here:
+
+sprintf is made to take a format definition and some values for the dynamic
+parts in this definition to produce a new string. A simple example in Perl
+would be:
+
+ perl -e'print sprintf("%s is my search engine!","DuckDuckGo")."\n";'
+
+The output of this will be:
+
+ DuckDuckGo is my search engine!
+
+The B<%s> in the first parameter of sprintf, is replaced with the second
+parameter we have given to the sprintf call. This is a very simple example,
+B<%s> means B<string> in this case. Alternative options are:
+
+ %% a percent sign
+ %c a character with the given number
+ %s a string
+ %d a signed integer, in decimal
+ %u an unsigned integer, in decimal
+ %o an unsigned integer, in octal
+ %x an unsigned integer, in hexadecimal
+ %e a floating-point number, in scientific notation
+ %f a floating-point number, in fixed decimal notation
+ %g a floating-point number, in %e or %f notation
+
+You normally will only face B<%s> and probably B<%d> in the context of
+DuckDuckGo, so don't be scared about the other options.
+
+B<%d> is specific to display a number, as decimal, so if you would say:
+
+ sprintf("%d bottles of beer",99.0);
+
+In your language, where 99.0 defines a float number, you would still get back:
+
+ "99 bottles of beer"
+
+A very important point here is the option to give several parameters, AND
+reorder them in the usage, for example:
+
+ sprintf("From %s to %s",'A','B');
+ # returns "From A to B"
+
+This seems to force always B<$from> in the first %s that appears, and B<$to> in
+the second appreance of %s. If in some language for example the order for this
+case gets "other around", then we can't change the order in the code of course,
+or make a switch. Luckily sprintf allows us to use the data in other order then
+given, as you can see on this example:
+
+ sprintf("To %2$s from %1$s",'A','B');
+ # returns "To B from A"
+
+This tells sprintf to put the first extra value into the B<%1$s> and the second
+extra value into B<%2$s>. So, if a translation that hits several placeholders,
+needs a different order of them, it can use this syntax to achieve this.
+
+If you want to know more about sprintf, you can checkout the
+L<perldoc to sprintf|http://perldoc.perl.org/functions/sprintf.html>, but for
+anything related to tokens, you leave the placeholders always as is in the
+tokens. Deeper knowledge of sprintf is only required for people who define
+tokens.
+
+=head3 Combined tokens
+
+With the understandment of sprintf, we are now able to make tokens specific for
+special visual needs, like if they need additional HTML. An example could be:
+
+ <: l('%s for more info!', '<a href="...">' ~ l('Click here') ~ '</a>' :>
+
+Would give out:
+
+ <a href="...">Click here</a> for more info!
+
+Which allows us to exclude the HTML from the translation, and still give the
+translator enough freedom to define which part of his text is the text that
+should be made clickable (or colored differently or whatever).
+
+A bigger problem exist, if we combine those placeholders in combination with
+number placeholders, explained in the section L</Placeholders and plurality>.
+The concept forces to get the count that is used to determine the proper plural
+form must be set to the first position. Which could lead to a problem, like in
+this case:
+
+ $username has $count message.
+ $username has $count messages.
+
+If we would try to convert this to B<"%s has %d messages.">, we would run into
+the problem, that the first placeholder would be B<$username> and not
+B<$count>. To avoid this we can use the method of I<sprintf> to change the
+order of the values given for the placeholders, the right solution would be:
+
+ <: ln("%2$s has %1$d message.","%2$s has %1$d messages.",$count,$username) :>
+
+The very big disadvantage here is the organizational part. It is really complex
+to have all those tokens in the database and still reference which ones are
+staying together. It always requires lots of comments and further information.
+In some very awkward cases you a real extreme cascading of the tokens. In those
+cases it is really essential to generated B<context>, here a bigger example
+from our JavaScript:
+
+ lp('webelieve','%s believe in %s AND %s.',
+ '<a href="/about">' + lp('webelieve','We') + '</a>',
+ '<a href="/goodies">' + lp('webelieve','better search') + '</a>',
+ '<a href="http://donttrack.us">' + lp('webelieve','no tracking') + '</a>'
+ );
+
+Which is in gettext written this:
+
+ msgctxt "webelieve"
+ msgid "%s believe in %s AND %s."
+
+ msgctxt "webelieve"
+ msgid "We"
+
+ msgctxt "webelieve"
+ msgid "better search"
+
+ msgctxt "webelieve"
+ msgid "no tracking"
+
+You can now imagine, that without a bit more comment, it is very hard to get
+it right to translate those texts. Best is if the user additional has an URL
+to see the tokens in action. Especially in the flow of all untranslated tokens,
+which is what most users do to help us translate.
+
+Most combined tokens are gathered under one specific B<msgctxt>, in the
+translation interface, you can click on the context given in the interface to
+reach a page with all tokens of this specific context. Still we try to add
+comments to every token that describe the complete text context where the token
+is used.
+
+=head3 Token translation functions
+
+ l( msgid, ... )
+ ln( msgid, msgid_plural, count, ... )
+ lp( msgctxt, msgid, ... )
+ lnp( msgctxt, msgid, msgid_plural, count, ... )
+
+=head3 Token translation storage
+
+As described in the previous sections, the database of the community platform
+stores all the translations, which then gets generated to the I<po> files used
+by I<gettext> in our translation system. Here I show you the german
+translations of the examples from above from the I<po> that gets generated:
+
+ msgid "Monthly newsletter:"
+ msgstr "Monatlicher Newsletter:"
+
+ msgctxt "size"
+ msgid "Medium"
+ msgstr "Mittelgroß"
+
+ msgid "Hello %s!"
+ msgstr "Hallo %s!"
+
+ msgctxt "email"
+ msgid "You have %d message."
+ msgid_plural "You have %d messages."
+ msgstr[0] "Du hast %d Nachricht."
+ msgstr[1] "Du hast %d Nachrichten."
+
+ msgid "%2$s has %1$d message."
+ msgid_plural "%2$s has %1$d messages."
+ msgstr[0] "%2$s hat %1$d Nachricht."
+ msgstr[1] "%2$s hat %1$d Nachrichten."
+
+ msgid "From %s to %s"
+ msgstr "Von %s nach %s"
+
+The pirate translation of the B<"Hello %s!"> example would look like this:
+
+ msgid "Hello %s!"
+ msgstr "%s, ahoi! hrrr"
+
+Our example to change the order of the placeholders would look like this:
+
+ msgid "From %s to %s"
+ msgstr "To %2$s from %1$s"
+
+In the case of a language which has more than 2 plural forms (See L</Plurality>), the
+number in the brackets will just get stacked up:
+
+ msgid "You have %d message."
+ msgid_plural "You have %d messages."
+ msgstr[0] "Mas %d spravu."
+ msgstr[1] "Mas %d spravy."
+ msgstr[2] "Mas %d sprav."
+
+Interesting sidenote, the highest amount of plural forms is 6. You can find this in
+the L<arabic language|https://duckduckgo.com/?q=arabic>.
+
+In the case of the placeholder with count and a dynamic text, a translation
+which uses the order correct, can of course then use the normal I<sprintf>
+placeholders definition:
+
+ msgid "%2$s has %1$d message."
+ msgid_plural "%2$s has %1$d messages."
+ msgstr[0] "%d Nachricht hat %s."
+ msgstr[1] "%d Nachrichten hat %s."
+
+=head3 Voting translations
+
+On the community platform, you are able to vote for an existing translation,
+instead of making your own translation. If you, by mistake, not saw the
+existing translation, your translation will automatically get converted to a
+note for this translation.
+
+=head3 Used translation
+
+The system which generates the translation I<po> files for all the languages,
+picks the translation by finding the translation with the most votes. If there
+are several translations with the same amount of votes, the translation will be
+used, where the translator has the highest B<grade> in this language. We will
+explain this in the community platform section (L</The community platform>)
+more deeply. This process happens on the release of the translations.
+
+=head3 Releasing translations
+
+I<This section is very technical, and can be skipped. Just go directly to
+L</Token domains>.>
+
+The generated I<po> files will be packed together with all other necessary data
+files, like the I<mo> file and the I<json> file for the JavaScript, as a Perl
+distribution for our central B<DuckPAN> server, which is used to fetch the
+releases of the open source development to our live systems. The code for this
+procedures is in the package L<DDGC::LocaleDist|https://github.com/duckduckgo/community-platform/blob/master/lib/DDGC/LocaleDist.pm>
+in the L<source code of the community platform|https://github.com/duckduckgo/community-platform>.
+
+=head2 Token domains
+
+For organizational reasons, but also for technical reasons, we are required to
+group the tokens in so called B<token domains>. The terminology is taken from
+I<gettext>, which also defines one I<po> file is one specific domain. We
+normally define a default domain on initalization of our translation library.
+This way we never need to care about the domain on the function calls for the
+translation library.
+
+Also the release of the translations (See L<|/Releasing translations>) is
+executed for every token domain individually. On the live systems we install
+all the latest releases of all token domains at once.
+
+=head3 Token domains in the community platform
+
+In the community platform (L</The community platform>) the different token
+domains are an independent list of tokens. This allows translators to pick the
+block of tokens, they want to work on. It is very important to understand that
+a half done translation is nearly as usable as no translation at all. The
+translators must be encouraged to finish up all tokens of a domain, only then
+it really makes sense to promote wider the specific project part to the users.
+We explain more about this in the section L</The community platform>.
+
+=head3 Token domain translation functions
+
+I<This section is very technical, and can be skipped. Just go directly to
+L</Overlapping tokens>.>
+
+Our translation library (See L<|/Locale::Simple>) also supports functions which
+use a specific token domain directly as parameter, but those functions are (so
+far) never used at DuckDuckGo. A switch between several domains inside one code
+file or template is avoided. They are called B<ld()>, B<ldn()>, B<ldp()> and
+B<ldnp()> and work exactly like already described translation functions, but
+take the name for the token domain that has to be used as first parameter. The
+function to set the default token domain is B<ltd()>.
+
+=head3 Overlapping tokens
+
+The definition of a token is bound to its token domain. This means, that 2
+identical tokens in 2 domains are still 2 independent tokens and still both
+need to be translated. In most cases, this sadly leads to a pointless
+translation of the exactly same meaning and probably even in the exactly same
+context. It is very hard to avoid this.
+
+Still there are cases left where the context given with the token domain might
+slighty fix the interpretation more deeply. Only because a token seems very
+identical in the english language does not directly lead to the same
+interpretation with other languages.
+
+=head2 The community platform
+
View
62 lib/DDG/Meta/AnyBlock.pm
@@ -1,31 +1,31 @@
-package DDG::Meta::AnyBlock;
-# ABSTRACT: Implement L<DDG::Block::Blockable::Any> to the plugin
-
-use strict;
-use warnings;
-use Carp;
-require Moo::Role;
-
-=head1 DESCRIPTION
-
-=method apply_keywords
-
-Adds the role L<DDG::Block::Blockable::Any> to the target classname. It's
-named I<apply_keywords> to be the same as in the other meta classes which
-actually really install keywords.
-
-=cut
-
-my %applied;
-
-sub apply_keywords {
- my ( $class, $target ) = @_;
-
- return if exists $applied{$target};
- $applied{$target} = undef;
-
- Moo::Role->apply_role_to_package($target,'DDG::Block::Blockable::Any');
-
-}
-
-1;
+package DDG::Meta::AnyBlock;
+# ABSTRACT: Implement L<DDG::Block::Blockable::Any> to the plugin
+
+use strict;
+use warnings;
+use Carp;
+require Moo::Role;
+
+=head1 DESCRIPTION
+
+=method apply_keywords
+
+Adds the role L<DDG::Block::Blockable::Any> to the target classname. It's
+named I<apply_keywords> to be the same as in the other meta classes which
+actually really install keywords.
+
+=cut
+
+my %applied;
+
+sub apply_keywords {
+ my ( $class, $target ) = @_;
+
+ return if exists $applied{$target};
+ $applied{$target} = undef;
+
+ Moo::Role->apply_role_to_package($target,'DDG::Block::Blockable::Any');
+
+}
+
+1;
View
24 lib/DDG/Region.pm
@@ -1,13 +1,13 @@
-package DDG::Region;
-# ABSTRACT: A region, can be empty [TODO]
-
-use Moo;
-
-my @region_attributes = qw();
-
-has $_ => (
- is => 'ro',
- default => sub { '' }
-) for (@region_attributes);
-
+package DDG::Region;
+# ABSTRACT: A region, can be empty [TODO]
+
+use Moo;
+
+my @region_attributes = qw();
+
+has $_ => (
+ is => 'ro',
+ default => sub { '' }
+) for (@region_attributes);
+
1;
View
172 lib/DDG/Test/Language.pm
@@ -1,87 +1,87 @@
-package DDG::Test::Language;
-# ABSTRACT: Gives functions for getting test L<DDG::Language> objects.
-
-use strict;
-use warnings;
-use DDG::Language;
-use Package::Stash;
-use utf8;
-
-=head1 DESCRIPTION
-
-Installs functions for getting test languages.
-
-B<Warning>: Be aware that you only use this module inside your test files in B<t/>.
-
-=cut
-
-our %languages = (
- 'us' => {
- 'flagicon' => 'us',
- 'flag_url' => 'https://duckduckgo.com/f2/us.png',
- 'name_in_local' => 'English of United States',
- 'rtl' => 0,
- 'locale' => 'en_US',
- 'nplurals' => 2,
- 'name_in_english' => 'English of United States',
- },
- 'de' => {
- 'flagicon' => 'de',
- 'flag_url' => 'https://duckduckgo.com/f2/de.png',
- 'name_in_local' => 'Deutsch von Deutschland',
- 'rtl' => 0,
- 'locale' => 'de_DE',
- 'nplurals' => 2,
- 'name_in_english' => 'German of Germany',
- },
- 'my' => {
- 'flagicon' => 'my',
- 'flag_url' => 'https://duckduckgo.com/f2/my.png',
- 'name_in_local' => 'Bahasa Malaysia di Malaysia',
- 'rtl' => 0,
- 'locale' => 'ms_MY',
- 'nplurals' => 1,
- 'name_in_english' => 'Malay in Malaysia',
- },
-);
-
-sub import {
- my ( $class, %params ) = @_;
- my $target = caller;
- my $stash = Package::Stash->new($target);
-
-=keyword test_language
-
-Gives back an example L<DDG::Location> defined by the first parameter.
-Possible values are B<us>, B<de> and B<my>.
-
-=cut
-
- $stash->add_symbol('&test_language', sub {
- my $language_key = shift;
- die "Unknown language_key \"".$language_key."\"" unless defined $languages{$language_key};
- return DDG::Language->new( %{$languages{$language_key}} );
- });
-
-=keyword test_language_by_env
-
-Will give back a L<DDG::Language> like </test_language> but will take the
-location definition by the ENV variable B<DDG_TEST_LANGUAGE>. If none is
-given then B<us> will be assumed. L<App::DuckPAN> is also using this
-function for getting the language for the sample requests, so you can
-set another location with prefixing your startup of B<server> or B<query>
-with the ENV variable:
-
- DDG_TEST_LANGUAGE=de duckpan server
-
-=cut
-
- $stash->add_symbol('&test_language_by_env', sub {
- my $language_key = defined $ENV{DDG_TEST_LANGUAGE} ? $ENV{DDG_TEST_LANGUAGE} : 'us';
- $stash->get_symbol('&test_language')->($language_key);
- });
-
-}
-
-1;
+package DDG::Test::Language;
+# ABSTRACT: Gives functions for getting test L<DDG::Language> objects.
+
+use strict;
+use warnings;
+use DDG::Language;
+use Package::Stash;
+use utf8;
+
+=head1 DESCRIPTION
+
+Installs functions for getting test languages.
+
+B<Warning>: Be aware that you only use this module inside your test files in B<t/>.
+
+=cut
+
+our %languages = (
+ 'us' => {
+ 'flagicon' => 'us',
+ 'flag_url' => 'https://duckduckgo.com/f2/us.png',
+ 'name_in_local' => 'English of United States',
+ 'rtl' => 0,
+ 'locale' => 'en_US',
+ 'nplurals' => 2,
+ 'name_in_english' => 'English of United States',
+ },
+ 'de' => {
+ 'flagicon' => 'de',
+ 'flag_url' => 'https://duckduckgo.com/f2/de.png',
+ 'name_in_local' => 'Deutsch von Deutschland',
+ 'rtl' => 0,
+ 'locale' => 'de_DE',
+ 'nplurals' => 2,
+ 'name_in_english' => 'German of Germany',
+ },
+ 'my' => {
+ 'flagicon' => 'my',
+ 'flag_url' => 'https://duckduckgo.com/f2/my.png',
+ 'name_in_local' => 'Bahasa Malaysia di Malaysia',
+ 'rtl' => 0,
+ 'locale' => 'ms_MY',
+ 'nplurals' => 1,
+ 'name_in_english' => 'Malay in Malaysia',
+ },
+);
+
+sub import {
+ my ( $class, %params ) = @_;
+ my $target = caller;
+ my $stash = Package::Stash->new($target);
+
+=keyword test_language
+
+Gives back an example L<DDG::Location> defined by the first parameter.
+Possible values are B<us>, B<de> and B<my>.
+
+=cut
+
+ $stash->add_symbol('&test_language', sub {
+ my $language_key = shift;
+ die "Unknown language_key \"".$language_key."\"" unless defined $languages{$language_key};
+ return DDG::Language->new( %{$languages{$language_key}} );
+ });
+
+=keyword test_language_by_env
+
+Will give back a L<DDG::Language> like </test_language> but will take the
+location definition by the ENV variable B<DDG_TEST_LANGUAGE>. If none is
+given then B<us> will be assumed. L<App::DuckPAN> is also using this
+function for getting the language for the sample requests, so you can
+set another location with prefixing your startup of B<server> or B<query>
+with the ENV variable:
+
+ DDG_TEST_LANGUAGE=de duckpan server
+
+=cut
+
+ $stash->add_symbol('&test_language_by_env', sub {
+ my $language_key = defined $ENV{DDG_TEST_LANGUAGE} ? $ENV{DDG_TEST_LANGUAGE} : 'us';
+ $stash->get_symbol('&test_language')->($language_key);
+ });
+
+}
+
+1;
View
230 lib/DDG/Test/Location.pm
@@ -1,116 +1,116 @@
-package DDG::Test::Location;
-# ABSTRACT: Gives functions for getting test L<DDG::Location> objects.
-
-use strict;
-use warnings;
-use DDG::Location;
-use Package::Stash;
-use utf8;
-
-=head1 DESCRIPTION
-
-Installs functions for getting test locations.
-
-B<Warning>: Be aware that you only use this module inside your test files in B<t/>.
-
-=cut
-
-our %locations = (
- 'us' => {
- country_code => 'US',
- country_code3 => 'USA',
- country_name => 'United States',
- region => 'PA',
- region_name => 'Pennsylvania',
- city => 'Phoenixville',
- postal_code => '19460',
- latitude => '40.1246',
- longitude => '-75.5385',
- time_zone => 'America/New_York',
- area_code => '610',
- continent_code => 'NA',
- metro_code => '504',
- },
- 'de' => {
- country_code => 'DE',
- country_code3 => 'DEU',
- country_name => 'Germany',
- region => '07',
- region_name => 'Nordrhein-Westfalen',
- city => 'Mönchengladbach',
- latitude => '51.2000',
- longitude => '6.4333',
- time_zone => 'Europe/Berlin',
- area_code => 0,
- continent_code => 'EU',
- metro_code => 0,
- },
- 'my' => {
- country_code => 'MY',
- country_code3 => 'MYS',
- country_name => 'Malaysia',
- region => '14',
- region_name => 'Kuala Lumpur',
- city => 'Kuala Lumpur',
- latitude => '3.1667',
- longitude => '101.7000',
- area_code => 0,
- continent_code => 'AS',
- metro_code => 0,
- },
- 'in' => {
- country_code => 'IN',
- country_code3 => 'IND',
- country_name => 'India',
- region => '07',
- region_name => 'Delhi',
- city => 'New Delhi',
- latitude => '28.6000',
- longitude => '77.2000',
- time_zone => 'Asia/Calcutta',
- area_code => 0,
- continent_code => 'AS',
- metro_code => 0,
- },
-);
-
-sub import {
- my ( $class, %params ) = @_;
- my $target = caller;
- my $stash = Package::Stash->new($target);
-
-=keyword test_location
-
-Gives back an example L<DDG::Location> defined by the first parameter.
-Possible values are B<us>, B<de>, B<in> and B<my>.
-
-=cut
-
- $stash->add_symbol('&test_location', sub {
- my $location_key = shift;
- die "Unknown location_key \"".$location_key."\"" unless defined $locations{$location_key};
- return DDG::Location->new( %{$locations{$location_key}} );
- });
-
-=keyword test_location_by_env
-
-Will give back a L<DDG::Location> like </test_location> but will take the
-location definition by the ENV variable B<DDG_TEST_LOCATION>. If none is
-given then B<us> will be assumed. L<App::DuckPAN> is also using this
-function for getting the location for the sample requests, so you can
-set another location with prefixing your startup of B<server> or B<query>
-with the ENV variable:
-
- DDG_TEST_LOCATION=de duckpan server
-
-=cut
-
- $stash->add_symbol('&test_location_by_env', sub {
- my $location_key = defined $ENV{DDG_TEST_LOCATION} ? $ENV{DDG_TEST_LOCATION} : 'us';
- $stash->get_symbol('&test_location')->($location_key);
- });
-
-}
-
-1;
+package DDG::Test::Location;
+# ABSTRACT: Gives functions for getting test L<DDG::Location> objects.
+
+use strict;
+use warnings;
+use DDG::Location;
+use Package::Stash;
+use utf8;
+
+=head1 DESCRIPTION
+
+Installs functions for getting test locations.
+
+B<Warning>: Be aware that you only use this module inside your test files in B<t/>.
+
+=cut
+
+our %locations = (
+ 'us' => {
+ country_code => 'US',
+ country_code3 => 'USA',
+ country_name => 'United States',
+ region => 'PA',
+ region_name => 'Pennsylvania',
+ city => 'Phoenixville',
+ postal_code => '19460',
+ latitude => '40.1246',
+ longitude => '-75.5385',
+ time_zone => 'America/New_York',
+ area_code => '610',
+ continent_code => 'NA',
+ metro_code => '504',
+ },
+ 'de' => {
+ country_code => 'DE',
+ country_code3 => 'DEU',
+ country_name => 'Germany',
+ region => '07',
+ region_name => 'Nordrhein-Westfalen',
+ city => 'Mönchengladbach',
+ latitude => '51.2000',
+ longitude => '6.4333',
+ time_zone => 'Europe/Berlin',
+ area_code => 0,
+ continent_code => 'EU',
+ metro_code => 0,
+ },
+ 'my' => {
+ country_code => 'MY',
+ country_code3 => 'MYS',
+ country_name => 'Malaysia',
+ region => '14',
+ region_name => 'Kuala Lumpur',
+ city => 'Kuala Lumpur',
+ latitude => '3.1667',
+ longitude => '101.7000',
+ area_code => 0,
+ continent_code => 'AS',
+ metro_code => 0,
+ },
+ 'in' => {
+ country_code => 'IN',
+ country_code3 => 'IND',
+ country_name => 'India',
+ region => '07',
+ region_name => 'Delhi',
+ city => 'New Delhi',
+ latitude => '28.6000',
+ longitude => '77.2000',
+ time_zone => 'Asia/Calcutta',
+ area_code => 0,
+ continent_code => 'AS',
+ metro_code => 0,
+ },
+);
+
+sub import {
+ my ( $class, %params ) = @_;
+ my $target = caller;
+ my $stash = Package::Stash->new($target);
+
+=keyword test_location
+
+Gives back an example L<DDG::Location> defined by the first parameter.
+Possible values are B<us>, B<de>, B<in> and B<my>.
+
+=cut
+
+ $stash->add_symbol('&test_location', sub {
+ my $location_key = shift;
+ die "Unknown location_key \"".$location_key."\"" unless defined $locations{$location_key};
+ return DDG::Location->new( %{$locations{$location_key}} );
+ });
+
+=keyword test_location_by_env
+
+Will give back a L<DDG::Location> like </test_location> but will take the
+location definition by the ENV variable B<DDG_TEST_LOCATION>. If none is
+given then B<us> will be assumed. L<App::DuckPAN> is also using this
+function for getting the location for the sample requests, so you can
+set another location with prefixing your startup of B<server> or B<query>
+with the ENV variable:
+
+ DDG_TEST_LOCATION=de duckpan server
+
+=cut
+
+ $stash->add_symbol('&test_location_by_env', sub {
+ my $location_key = defined $ENV{DDG_TEST_LOCATION} ? $ENV{DDG_TEST_LOCATION} : 'us';
+ $stash->get_symbol('&test_location')->($location_key);
+ });
+
+}
+
+1;
View
78 lib/DDG/ZeroClickInfo/Spice/Data.pm
@@ -1,40 +1,40 @@
-package DDG::ZeroClickInfo::Spice::Data;
-# ABSTRACT: Data that gets delivered additional to the spice call into the Javascript of the HTML
-
-use Moo;
-
-=head1 SYNOPSIS
-
-Inside your spice handler
-
- return $path_part_one, $path_part_two, data(
- key => "value",
- more_key => "more value",
- most_key => "most value - buy now!",
- );
-
-=attr data
-
-Needs a hashref of the data you want to access inside the javascript.
-
-=cut
-
-has data => (
- is => 'ro',
- required => 1,
-);
-
-=method add_data
-
-Integrates the given B<DDG::ZeroClickInfo::Spice::Data> into data object. The
-newer one always overrides variables already set.
-
-=cut
-
-sub add_data {
- my ( $self, $data ) = @_;
- die "can only handle DDG::ZeroClickInfo::Spice::Data" unless ref $data eq 'DDG::ZeroClickInfo::Spice::Data';
- $self->data->{$_} = $data->data->{$_} for (keys %{$data->data});
-}
-
+package DDG::ZeroClickInfo::Spice::Data;
+# ABSTRACT: Data that gets delivered additional to the spice call into the Javascript of the HTML
+
+use Moo;
+
+=head1 SYNOPSIS
+
+Inside your spice handler
+
+ return $path_part_one, $path_part_two, data(
+ key => "value",
+ more_key => "more value",
+ most_key => "most value - buy now!",
+ );
+
+=attr data
+
+Needs a hashref of the data you want to access inside the javascript.
+
+=cut
+
+has data => (
+ is => 'ro',
+ required => 1,
+);
+
+=method add_data
+
+Integrates the given B<DDG::ZeroClickInfo::Spice::Data> into data object. The
+newer one always overrides variables already set.
+
+=cut
+
+sub add_data {
+ my ( $self, $data ) = @_;
+ die "can only handle DDG::ZeroClickInfo::Spice::Data" unless ref $data eq 'DDG::ZeroClickInfo::Spice::Data';
+ $self->data->{$_} = $data->data->{$_} for (keys %{$data->data});
+}
+
1;
Please sign in to comment.
Something went wrong with that request. Please try again.