diff --git a/.gitattributes b/.gitattributes
index 4e795980d..6930ab13c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -7,3 +7,4 @@
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
+/docs export-ignore
diff --git a/.gitignore b/.gitignore
index 5de5676cf..2d345fb66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
phpunit.xml
composer.lock
vendor/
+docs/_build/
diff --git a/README.md b/README.md
index 88e772c33..c29400e8f 100644
--- a/README.md
+++ b/README.md
@@ -15,360 +15,11 @@ or a Doctrine DBAL (+Cache) based backend.
When you create an I18N route and you go on it with your browser, the locale will be updated.
-## Installation
+## Documentation
-```bash
-composer.phar require besimple/i18n-routing-bundle
-```
-
-```php
-//app/AppKernel.php
-public function registerBundles()
-{
- $bundles = array(
- //...
- new BeSimple\I18nRoutingBundle\BeSimpleI18nRoutingBundle(),
- );
-}
-```
-
-### Update your configuration
-
-```yaml
-# app/config/config.yml
-be_simple_i18n_routing: ~
-```
-
-## Create your routing
-
-To define internationalized routes in XML or YAML, you need to import the
-routing file by using the ``be_simple_i18n`` type:
-
-```yaml
-my_yaml_i18n_routes:
- resource: "@MyWebsiteBundle/Resources/config/routing/i18n.yml"
- type: be_simple_i18n
- prefix:
- en: /website
- fr: /site
- de: /webseite
-my_xml_i18n_routes:
- resource: "@MyWebsiteBundle/Resources/config/routing/i18n.xml"
- type: be_simple_i18n
-```
-
-You can optionally specify a prefix or translated prefixes as shown above.
-
-### Yaml routing file
-
-```yaml
-homepage:
- locales: { en: "/welcome", fr: "/bienvenue", de: "/willkommen" }
- defaults: { _controller: MyWebsiteBundle:Frontend:index }
-```
-
-### XML routing file
-
-```xml
-
-
-
-
- /welcome
- /bienvenue
- /willkommen
- MyWebsiteBundle:Frontend:index
-
-
-```
-
-Note that the XML file uses a different namespace than when using the core
-loader: ``http://besim.pl/schema/i18n_routing``.
-
-### PHP routing file
-
-```php
-addCollection(
- $generator->generateRoutes(
- 'homepage',
- array('en' => '/welcome', 'fr' => '/bienvenue', 'de' => '/willkommen'),
- new Route('', array(
- '_controller' => 'MyWebsiteBundle:Frontend:index'
- ))
- )
-);
-
-return $collection;
-```
-
-### Controller annotations
-
-Annotation loading is only supported for Symfony 2.5 and greater and needs to be enabled as followed.
-```YAML
-# app/config/config.yml
-be_simple_i18n_routing:
- annotations: true
-```
-
-```PHP
-use BeSimple\I18nRoutingBundle\Routing\Annotation\I18nRoute;
-
-class NoPrefixController
-{
- /**
- * @I18nRoute({ "en": "/welcome", "fr": "/bienvenue", "de": "/willkommen" }, name="homepage")
- */
- public function indexAction() { }
-}
-```
-
-### You can insert classic route in your routing
-
-#### Yaml routing file
-
-```yaml
-homepage:
- locales: { en: "/en/", fr: "/fr/", de: "/de/" }
- defaults: { _controller: HelloBundle:Frontend:homepage }
-
-welcome:
- locales: { en: "/welcome/{name}", fr: "/bienvenue/{name}", de: "/willkommen/{name}" }
- defaults: { _controller: MyWebsiteBundle:Frontend:welcome }
-```
-
-#### XML routing file
-
-```xml
-
-
-
-
-
- HelloBundle:Hello:index
-
-
- /welcome/{name}
- /bienvenue/{name}
- /willkommen/{name}
- MyWebsiteBundle:Frontend:index
-
-
-```
-
-#### PHP routing file
-
-```php
-add('hello', new Route('/hello/{name}', array(
- '_controller' => 'HelloBundle:Hello:index',
-)));
-$collection->addCollection(
- $generator->generateRoutes(
- 'homepage',
- array('en' => '/welcome/{name}', 'fr' => '/bienvenue/{name}', 'de' => '/willkommen/{name}'),
- new Route('', array(
- '_controller' => 'MyWebsiteBundle:Frontend:index',
- ))
- )
-);
-
-return $collection;
-```
-
-### Advanced locale support
-
-By default this bundle allows any locale to be used and there is no check if a locale is missing for a specific route.
-This is great but sometimes you may wish to be strict, let take a look at the following configuration:
-```YAML
-be_simple_i18n_routing:
- locales:
- supported: ['en', 'nl']
- filter: true
- strict: true
-```
-
-The `locales.supported` specifies which locales are supported.
-
-The `locales.filter` option is responsible for filtering out any unknown locales so only routes for 'en' and 'nl' are available.
-
-The `locales.strict` option when set to `true` is responsible for throwing a exception when a i18n route is found where the locale is unknown or where a locale is missing.
-This option can also be set to `null` to disable locale is missing for a route exception and `false` to disable exceptions.
-
-### Route naming
-
-By default all routes that are imported are named '.' but sometimes you may want to change this behaviour.
-To do this you can specify a route name inflector service in your configuration as followed.
-```YAML
-be_simple_i18n_routing:
- route_name_inflector: 'my_route_name_inflector_service'
-```
-*The service must implement the `BeSimple\I18nRoutingBundle\Routing\RouteGenerator\NameInflector\RouteNameInflectorInterface` interface.*
-
-There are currently 2 inflectors available by default [`be_simple_i18n_routing.route_name_inflector.postfix`](src/Routing/RouteGenerator/NameInflector/PostfixInflector.php) and [`be_simple_i18n_routing.route_name_inflector.default_postfix`](src/Routing/RouteGenerator/NameInflector/DefaultPostfixInflector.php).
-
-#### Default postfix inflector
-The default postfix inflector changed the behaviour of to only add a locale postfix when the locale is not the default locale.
-A example configuration is as followed.
-```YAML
-be_simple_i18n_routing:
- route_name_inflector: 'my_route_name_inflector_service'
- locales:
- default_locale: '%kernel.default_locale%'
-```
-
-## Generate a route in your templates
-
-### Specify a locale
-
-#### Twig
-
- {{ path('homepage.en') }}
- {{ path('homepage', { 'locale': 'en' }) }}
- {{ path('homepage.fr') }}
- {{ path('homepage', { 'locale': 'fr' }) }}
- {{ path('homepage.de') }}
- {{ path('homepage', { 'locale': 'de' }) }}
-
-#### PHP
-
-```php
-generate('homepage.en') ?>
-generate('homepage', array('locale' => 'en')) ?>
-generate('homepage.fr') ?>
-generate('homepage', array('locale' => 'fr')) ?>
-generate('homepage.de') ?>
-generate('homepage', array('locale' => 'de')) ?>
-```
-
-### Use current locale of user
-
-#### Twig
-
- {{ path('homepage') }}
-
-#### PHP
-
-```php
-generate('homepage') ?>
-```
-
-## Translating the route attributes
-
-If the static parts of your routes are translated you get to the point really
-fast when dynamic parts such as product slugs, category names or other dynamic
-routing parameters should be translated. The bundle provides 2 implementations.
-
-After configuring the backend you want to use (see below for each one), you
-can define a to be translated attribute in your route defaults:
-
-```yaml
-product_view:
- locales: { en: "/product/{slug}", de: "/produkt/{slug}" }
- defaults: { _controller: "ShopBundle:Product:view", _translate: "slug" }
-product_view2:
- locales: { en: "/product/{category}/{slug}", de: "/produkt/{category}/{slug}" }
- defaults:
- _controller: "ShopBundle:Product:view"
- _translate: ["slug", "category"]
-```
-
-The same goes with generating routes, now backwards:
-
- {{ path("product_view", {"slug": product.slug, "translate": "slug"}) }}
- {{ path("product_view2", {"slug": product.slug, "translate": ["slug", "category]}) }}
-
-The reverse translation is only necessary if you have the "original" values
-in your templates. If you have access to the localized value of the current
-locale then you can just pass this and do not hint to translate it with the
-"translate" key.
-
-### Doctrine DBAL Backend
-
-Configure the use of the DBAL backend
-
-```yaml
-# app/config/config.yml
-be_simple_i18n_routing:
- attribute_translator:
- type: doctrine_dbal
- connection: default # Doctrine DBAL connection name. Using null (default value) will use the default connection
- cache: apc
-```
-
-The Doctrine Backend has the following table structure:
-
-```sql
-CREATE TABLE routing_translations (
- id INT NOT NULL,
- route VARCHAR(255) NOT NULL,
- locale VARCHAR(255) NOT NULL,
- attribute VARCHAR(255) NOT NULL,
- localized_value VARCHAR(255) NOT NULL,
- original_value VARCHAR(255) NOT NULL,
- UNIQUE INDEX UNIQ_291BA3522C420794180C698FA7AEFFB (route, locale, attribute),
- INDEX IDX_291BA352D951F3E4 (localized_value),
- PRIMARY KEY(id)
-) ENGINE = InnoDB;
-```
-
-Lookups are made through the combination of route name, locale and attribute
-of the route to be translated.
-
-Every lookup is cached in a Doctrine\Common\Cache\Cache instance that you
-should configure to be APC, Memcache or Xcache for performance reasons.
-
-If you are using Doctrine it automatically registers a listener for SchemaTool
-to create the routing_translations table for your database backend, you only
-have to call:
-
- ./app/console doctrine:schema:update --dump-sql
- ./app/console doctrine:schema:update --force
-
-### Translator backend
-
-This implementation uses the Symfony2 translator to translate the attributes.
-The translation domain will be created using the pattern `_`
-
-```yaml
-# app/config/config.yml
-be_simple_i18n_routing:
- attribute_translator:
- type: translator
-```
-
-### Custom backend
-
-If you want to use a different implementation, simply create a service implementing
-`BeSimple\I18nRoutingBundle\Routing\Translator\AttributeTranslatorInterface`.
-
-```yaml
-# app/config/config.yml
-be_simple_i18n_routing:
- attribute_translator:
- type: service
- id: my_attribute_translator
-```
+The documentation for this bundle is available in the `docs` directory of the bundle:
+* Read the [BeSimpleI18nRoutingBundle documentation](http://symfony.com/doc/master/bundles/KnpMenuBundle/index.html)
## License
diff --git a/composer.json b/composer.json
index 211a69d42..e418ce3ec 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"name": "besimple/i18n-routing-bundle",
"type": "symfony-bundle",
- "description": "Full routing internationalized on your Symfony2 project ",
+ "description": "Full routing internationalized on your Symfony2 project",
"keywords": ["routing", "internationalisation", "Symfony2", "BeSimpleI18nRoutingBundle"],
"homepage": "https://github.com/BeSimple/BeSimpleI18nRoutingBundle",
"license": "MIT",
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..81576969a
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,28 @@
+Documentation
+====================
+
+This directory contains documentation for BeSimple - I18n Routing Bundle Documentation, available on [**besimplei18nroutingbundle.readthedocs.io**](http://besimplei18nroutingbundle.readthedocs.io/).
+
+It is hosted by the great [readthedocs.org](http://readthedocs.org).
+
+Issues
+------
+
+The documentation uses [GitHub issues](https://github.com/BeSimple/BeSimpleI18nRoutingBundle/issues).
+
+Build
+-----
+
+If you contribute to documentation you will want to check how it looks after your changes.
+To be able to build the documentation, install [Sphinx](http://sphinx-doc.org/).
+
+1. [Install `pip`, Python package manager](https://pip.pypa.io/en/stable/installing/)
+2. Download documentation requirements: `$ pip install -r requirements.txt`
+3. Build the documentation:`$ sphinx-build -b html . build`
+
+Documentation index is `build/index.html`.
+
+Authors
+-------
+
+See the list of [our amazing contributors](http://github.com/BeSimple/BeSimpleI18nRoutingBundle/contributors).
diff --git a/docs/_theme/_exts/LICENSE b/docs/_theme/_exts/LICENSE
new file mode 100644
index 000000000..bc6ad0497
--- /dev/null
+++ b/docs/_theme/_exts/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2015 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/docs/_theme/_exts/sensio/__init__.py b/docs/_theme/_exts/sensio/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/_theme/_exts/sensio/sphinx/__init__.py b/docs/_theme/_exts/sensio/sphinx/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/_theme/_exts/sensio/sphinx/bestpractice.py b/docs/_theme/_exts/sensio/sphinx/bestpractice.py
new file mode 100644
index 000000000..8297aff4d
--- /dev/null
+++ b/docs/_theme/_exts/sensio/sphinx/bestpractice.py
@@ -0,0 +1,42 @@
+from docutils.parsers.rst import Directive, directives
+from docutils import nodes
+from string import upper
+from sphinx.util.compat import make_admonition
+from sphinx import addnodes
+from sphinx.locale import _
+
+class bestpractice(nodes.Admonition, nodes.Element):
+ pass
+
+class BestPractice(Directive):
+ has_content = True
+ required_arguments = 0
+ optional_arguments = 1
+ final_argument_whitespace = True
+ option_spec = {}
+
+ def run(self):
+ ret = make_admonition(
+ bestpractice, self.name, [_('Best Practice')], self.options,
+ self.content, self.lineno, self.content_offset, self.block_text,
+ self.state, self.state_machine)
+ if self.arguments:
+ argnodes, msgs = self.state.inline_text(self.arguments[0],
+ self.lineno)
+ para = nodes.paragraph()
+ para += argnodes
+ para += msgs
+ ret[0].insert(1, para)
+
+ return ret
+
+def visit_bestpractice_node(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS=('admonition best-practice')))
+ self.set_first_last(node)
+
+def depart_bestpractice_node(self, node):
+ self.depart_admonition(node)
+
+def setup(app):
+ app.add_node(bestpractice, html=(visit_bestpractice_node, depart_bestpractice_node))
+ app.add_directive('best-practice', BestPractice)
diff --git a/docs/_theme/_exts/sensio/sphinx/codeblock.py b/docs/_theme/_exts/sensio/sphinx/codeblock.py
new file mode 100644
index 000000000..0dc8e9dd7
--- /dev/null
+++ b/docs/_theme/_exts/sensio/sphinx/codeblock.py
@@ -0,0 +1,19 @@
+"""
+ :copyright: (c) 2010-2015 Fabien Potencier
+ :license: MIT, see LICENSE for more details.
+"""
+
+from sphinx.directives.code import CodeBlock
+
+"""
+A wrapper around the built-in CodeBlock class to always
+enable line numbers.
+"""
+class NumberedCodeBlock(CodeBlock):
+ def run(self):
+ self.options['linenos'] = 'table'
+ return super(NumberedCodeBlock, self).run();
+
+def setup(app):
+ app.add_directive('code-block', NumberedCodeBlock)
+ app.add_directive('sourcecode', NumberedCodeBlock)
diff --git a/docs/_theme/_exts/sensio/sphinx/configurationblock.py b/docs/_theme/_exts/sensio/sphinx/configurationblock.py
new file mode 100644
index 000000000..fc01ae29c
--- /dev/null
+++ b/docs/_theme/_exts/sensio/sphinx/configurationblock.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+"""
+ :copyright: (c) 2010-2012 Fabien Potencier
+ :license: MIT, see LICENSE for more details.
+"""
+
+from docutils.parsers.rst import Directive, directives
+from docutils import nodes
+
+class configurationblock(nodes.General, nodes.Element):
+ pass
+
+class ConfigurationBlock(Directive):
+ has_content = True
+ required_arguments = 0
+ optional_arguments = 0
+ final_argument_whitespace = True
+ option_spec = {}
+ formats = {
+ 'html': 'HTML',
+ 'xml': 'XML',
+ 'php': 'PHP',
+ 'yaml': 'YAML',
+ 'jinja': 'Twig',
+ 'html+jinja': 'Twig',
+ 'jinja+html': 'Twig',
+ 'twig': 'Twig',
+ 'html+twig': 'Twig',
+ 'twig+html': 'Twig',
+ 'php+html': 'PHP',
+ 'html+php': 'PHP',
+ 'ini': 'INI',
+ 'php-annotations': 'Annotations',
+ 'php-standalone': 'Standalone Use',
+ 'php-symfony': 'Framework Use',
+ }
+
+ def __init__(self, *args):
+ Directive.__init__(self, *args)
+ env = self.state.document.settings.env
+ config_block = env.app.config.config_block
+
+ for language in config_block:
+ self.formats[language] = config_block[language]
+
+ def run(self):
+ env = self.state.document.settings.env
+
+ node = nodes.Element()
+ node.document = self.state.document
+ self.state.nested_parse(self.content, self.content_offset, node)
+
+ entries = []
+ for i, child in enumerate(node):
+ if isinstance(child, nodes.literal_block):
+ # add a title (the language name) before each block
+ #targetid = "configuration-block-%d" % env.new_serialno('configuration-block')
+ #targetnode = nodes.target('', '', ids=[targetid])
+ #targetnode.append(child)
+ if 'language' in child:
+ language = child['language']
+ else:
+ language = env.app.config.highlight_language
+
+ innernode = nodes.emphasis(self.formats[language], self.formats[language])
+
+ para = nodes.paragraph()
+ para += [innernode, child]
+
+ entry = nodes.list_item('')
+ entry.append(para)
+ entries.append(entry)
+
+ resultnode = configurationblock()
+ resultnode.append(nodes.bullet_list('', *entries))
+
+ return [resultnode]
+
+def visit_configurationblock_html(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='configuration-block'))
+
+def depart_configurationblock_html(self, node):
+ self.body.append('\n')
+
+def visit_configurationblock_latex(self, node):
+ pass
+
+def depart_configurationblock_latex(self, node):
+ pass
+
+def setup(app):
+ app.add_node(configurationblock,
+ html=(visit_configurationblock_html, depart_configurationblock_html),
+ latex=(visit_configurationblock_latex, depart_configurationblock_latex))
+ app.add_directive('configuration-block', ConfigurationBlock)
+ app.add_config_value('config_block', {}, 'env')
diff --git a/docs/_theme/_exts/sensio/sphinx/php.py b/docs/_theme/_exts/sensio/sphinx/php.py
new file mode 100644
index 000000000..9c84acc5c
--- /dev/null
+++ b/docs/_theme/_exts/sensio/sphinx/php.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+"""
+ :copyright: (c) 2010-2012 Fabien Potencier
+ :license: MIT, see LICENSE for more details.
+"""
+
+from sphinx import addnodes
+from sphinx.domains import Domain, ObjType
+from sphinx.locale import l_, _
+from sphinx.directives import ObjectDescription
+from sphinx.domains.python import py_paramlist_re as js_paramlist_re
+from sphinx.roles import XRefRole
+from sphinx.util.nodes import make_refnode
+from sphinx.util.docfields import Field, GroupedField, TypedField
+
+def setup(app):
+ app.add_domain(PHPDomain)
+
+class PHPXRefRole(XRefRole):
+ def process_link(self, env, refnode, has_explicit_title, title, target):
+ # basically what sphinx.domains.python.PyXRefRole does
+ refnode['php:object'] = env.temp_data.get('php:object')
+ if not has_explicit_title:
+ title = title.lstrip('\\')
+ target = target.lstrip('~')
+ if title[0:1] == '~':
+ title = title[1:]
+ ns = title.rfind('\\')
+ if ns != -1:
+ title = title[ns+1:]
+ if target[0:1] == '\\':
+ target = target[1:]
+ refnode['refspecific'] = True
+ return title, target
+
+class PHPDomain(Domain):
+ """PHP language domain."""
+ name = 'php'
+ label = 'PHP'
+ # if you add a new object type make sure to edit JSObject.get_index_string
+ object_types = {
+ }
+ directives = {
+ }
+ roles = {
+ 'func': PHPXRefRole(fix_parens=True),
+ 'class': PHPXRefRole(),
+ 'data': PHPXRefRole(),
+ 'attr': PHPXRefRole(),
+ }
+ initial_data = {
+ 'objects': {}, # fullname -> docname, objtype
+ }
+
+ def clear_doc(self, docname):
+ for fullname, (fn, _) in self.data['objects'].items():
+ if fn == docname:
+ del self.data['objects'][fullname]
+
+ def find_obj(self, env, obj, name, typ, searchorder=0):
+ if name[-2:] == '()':
+ name = name[:-2]
+ objects = self.data['objects']
+ newname = None
+ if searchorder == 1:
+ if obj and obj + '\\' + name in objects:
+ newname = obj + '\\' + name
+ else:
+ newname = name
+ else:
+ if name in objects:
+ newname = name
+ elif obj and obj + '\\' + name in objects:
+ newname = obj + '\\' + name
+ return newname, objects.get(newname)
+
+ def resolve_xref(self, env, fromdocname, builder, typ, target, node,
+ contnode):
+ objectname = node.get('php:object')
+ searchorder = node.hasattr('refspecific') and 1 or 0
+ name, obj = self.find_obj(env, objectname, target, typ, searchorder)
+ if not obj:
+ return None
+ return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
+
+ def get_objects(self):
+ for refname, (docname, type) in self.data['objects'].iteritems():
+ yield refname, refname, type, docname, refname, 1
diff --git a/docs/_theme/_exts/sensio/sphinx/phpcode.py b/docs/_theme/_exts/sensio/sphinx/phpcode.py
new file mode 100644
index 000000000..08102cf52
--- /dev/null
+++ b/docs/_theme/_exts/sensio/sphinx/phpcode.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+"""
+ :copyright: (c) 2010-2012 Fabien Potencier
+ :license: MIT, see LICENSE for more details.
+"""
+
+import re
+
+from docutils import nodes, utils
+
+from sphinx.util.nodes import split_explicit_title
+
+def php_namespace_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ env = inliner.document.settings.env
+ has_explicit_title, title, namespace = split_explicit_title(text)
+
+ if len(re.findall(r'[^\\]\\[^\\]', rawtext)) > 0:
+ env.warn(env.docname, 'backslash not escaped in %s' % rawtext, lineno)
+
+ full_url = build_url('namespace', namespace, None, None, inliner)
+
+ if not has_explicit_title:
+ name = namespace.lstrip('\\')
+ ns = name.rfind('\\')
+ if ns != -1:
+ name = name[ns+1:]
+ title = name
+ list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=namespace)]
+ pnode = nodes.literal('', '', *list)
+ return [pnode], []
+
+def php_class_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ env = inliner.document.settings.env
+ has_explicit_title, title, full_class = split_explicit_title(text)
+ backslash = full_class.rfind('\\')
+ namespace = full_class[:backslash]
+ class_name = full_class[backslash+1:]
+
+ if len(re.findall(r'[^\\]\\[^\\]', rawtext)) > 0:
+ env.warn(env.docname, 'backslash not escaped in %s' % rawtext, lineno)
+
+ full_url = build_url('class', namespace, class_name, None, inliner)
+
+ if not has_explicit_title:
+ class_name = full_class.lstrip('\\')
+ ns = class_name.rfind('\\')
+ if ns != -1:
+ class_name = class_name[ns+1:]
+ title = class_name
+ list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class)]
+ pnode = nodes.literal('', '', *list)
+ return [pnode], []
+
+def php_method_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ env = inliner.document.settings.env
+ has_explicit_title, title, class_and_method = split_explicit_title(text)
+
+ ns = class_and_method.rfind('::')
+ full_class = class_and_method[:ns]
+ method = class_and_method[ns+2:]
+ backslash = full_class.rfind('\\')
+ namespace = full_class[:backslash]
+ class_name = full_class[backslash+1:]
+
+ if len(re.findall(r'[^\\]\\[^\\]', rawtext)) > 0:
+ env.warn(env.docname, 'backslash not escaped in %s' % rawtext, lineno)
+
+ full_url = build_url('method', namespace, class_name, method, inliner)
+
+ if not has_explicit_title:
+ title = method + '()'
+ list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class + '::' + method + '()')]
+ pnode = nodes.literal('', '', *list)
+ return [pnode], []
+
+def php_phpclass_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ has_explicit_title, title, full_class = split_explicit_title(text)
+
+ full_url = 'http://php.net/manual/en/class.%s.php' % full_class.lower()
+
+ if not has_explicit_title:
+ title = full_class
+ list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class)]
+ pnode = nodes.literal('', '', *list)
+ return [pnode], []
+
+def php_phpmethod_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ has_explicit_title, title, class_and_method = split_explicit_title(text)
+
+ ns = class_and_method.rfind('::')
+ full_class = class_and_method[:ns]
+ method = class_and_method[ns+2:]
+
+ full_url = 'http://php.net/manual/en/%s.%s.php' % (full_class.lower(), method.lower())
+
+ if not has_explicit_title:
+ title = full_class + '::' + method + '()'
+ list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class)]
+ pnode = nodes.literal('', '', *list)
+ return [pnode], []
+
+def php_phpfunction_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
+ has_explicit_title, title, full_function = split_explicit_title(text)
+
+ full_url = 'http://php.net/manual/en/function.%s.php' % full_function.replace('_', '-').lower()
+
+ if not has_explicit_title:
+ title = full_function
+ list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_function)]
+ pnode = nodes.literal('', '', *list)
+ return [pnode], []
+
+def setup(app):
+ app.add_config_value('api_url', {}, 'env')
+ app.add_config_value('api_url_pattern', None, 'env')
+ app.add_config_value('namespace_separator', '/', 'env')
+ app.add_role('namespace', php_namespace_role)
+ app.add_role('class', php_class_role)
+ app.add_role('method', php_method_role)
+ app.add_role('phpclass', php_phpclass_role)
+ app.add_role('phpmethod', php_phpmethod_role)
+ app.add_role('phpfunction', php_phpfunction_role)
+
+def build_url(role, namespace, class_name, method, inliner):
+ env = inliner.document.settings.env
+
+ if namespace is None:
+ namespace = ''
+ if class_name is None:
+ class_name = ''
+ if method is None:
+ method = ''
+
+ if ('namespace_separator' in env.app.config):
+ namespace = namespace.replace('\\', env.app.config.namespace_separator)
+ else:
+ namespace = namespace.replace('\\', '/')
+
+ if (env.app.config.api_url_pattern is None):
+ fqcn = '%(namespace)s{class}/%(class)s{/class}{method}/%(class)s{/method}'
+ api_url_pattern = env.app.config.api_url.replace('%s', fqcn)
+ api_url_pattern += '.html{method}#method_%(method)s{/method}'
+ else:
+ api_url_pattern = str(env.app.config.api_url_pattern)
+
+ api_url_pattern = api_url_pattern.replace('{'+role+'}', '')
+ api_url_pattern = api_url_pattern.replace('{/'+role+'}', '')
+
+ for unused_role in ('namespace', 'class', 'method'):
+ api_url_pattern = re.sub(r'{'+unused_role+'}.*?{/'+unused_role+'}', '', api_url_pattern)
+
+ try:
+ full_url = api_url_pattern % {'namespace': namespace, 'class': class_name, 'method': method}
+ except (TypeError, ValueError):
+ env.warn(env.docname, 'unable to expand %s api_url with base '
+ 'URL %r, please make sure the base contains \'%%s\' '
+ 'exactly once' % (role, api_url_pattern))
+ full_url = api_url_pattern + full_class
+
+ return full_url
diff --git a/docs/_theme/_exts/sensio/sphinx/refinclude.py b/docs/_theme/_exts/sensio/sphinx/refinclude.py
new file mode 100644
index 000000000..0b1d72086
--- /dev/null
+++ b/docs/_theme/_exts/sensio/sphinx/refinclude.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+"""
+ :copyright: (c) 2010-2012 Fabien Potencier
+ :license: MIT, see LICENSE for more details.
+"""
+
+from docutils.parsers.rst import Directive, directives
+from docutils import nodes
+
+class refinclude(nodes.General, nodes.Element):
+ pass
+
+class RefInclude(Directive):
+ has_content = False
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = False
+ option_spec = {}
+
+ def run(self):
+ document = self.state.document
+
+ if not document.settings.file_insertion_enabled:
+ return [document.reporter.warning('File insertion disabled',
+ line=self.lineno)]
+
+ env = self.state.document.settings.env
+ target = self.arguments[0]
+
+ node = refinclude()
+ node['target'] = target
+
+ return [node]
+
+def process_refinclude_nodes(app, doctree, docname):
+ env = app.env
+ for node in doctree.traverse(refinclude):
+ docname, labelid, sectname = env.domaindata['std']['labels'].get(node['target'],
+ ('','',''))
+
+ if not docname:
+ return [document.reporter.error('Unknown target name: "%s"' % node['target'],
+ line=self.lineno)]
+
+ resultnode = None
+ dt = env.get_doctree(docname)
+ for n in dt.traverse(nodes.section):
+ if labelid in n['ids']:
+ node.replace_self([n])
+ break
+
+def setup(app):
+ app.add_node(refinclude)
+ app.add_directive('include-ref', RefInclude)
+ app.connect('doctree-resolved', process_refinclude_nodes)
diff --git a/docs/_theme/_exts/setup.py b/docs/_theme/_exts/setup.py
new file mode 100644
index 000000000..49582d108
--- /dev/null
+++ b/docs/_theme/_exts/setup.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from setuptools import setup, find_packages
+
+setup(
+ name = 'sphinx-php',
+ version = '1.0',
+ author = 'Fabien Potencier',
+ author_email = 'fabien@symfony.com',
+ description = 'Sphinx Extensions for PHP and Symfony',
+ license = 'MIT',
+ packages = find_packages(),
+ install_requires = ['Sphinx>=0.6'],
+)
diff --git a/docs/_theme/_static/style.css b/docs/_theme/_static/style.css
new file mode 100644
index 000000000..361bfcaad
--- /dev/null
+++ b/docs/_theme/_static/style.css
@@ -0,0 +1,75 @@
+.clearfix:before, .clearfix:after {
+ content: " ";
+ display: table
+}
+
+.clearfix:after {
+ clear: both
+}
+
+div.configuration-block {
+ margin: 1em 0;
+ position: relative
+}
+
+div.configuration-block ul.simple {
+ margin: 0
+}
+
+div.configuration-block .simple li {
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ background-color: #f2f2f2;
+ float: left;
+ list-style: none;
+ margin: 0 7px 0 0;
+ padding: 0
+}
+
+div.configuration-block li em {
+ font-size: 90%;
+ font-style: normal
+}
+
+div.configuration-block .simple li a {
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ color: #000000;
+ display: block;
+ padding: 5px 12px 3px;
+ text-decoration: none
+}
+
+div.configuration-block .simple li.selected,
+div.configuration-block .simple li a:hover {
+ background-color: #c0bec3;
+ color: #000000;
+}
+div.configuration-block .selected a {
+ color: #000000;
+ text-decoration: none
+}
+
+div.configuration-block div.highlight pre {
+ margin: 0
+}
+
+div.configuration-block div {
+ left: 0;
+ position: absolute
+}
+
+div.configuration-block div div {
+ position: static
+}
+
+div.configuration-block .literal-block {
+ margin: 0 0 10px
+}
+
+@supports (overflow:-webkit-marquee) and (justify-content:inherit) {
+ div.configuration-block div.highlight pre {
+ margin: 0 1em 0 0;
+ padding: 1em 5em 1em 1em
+ }
+}
diff --git a/docs/_theme/_static/style.js b/docs/_theme/_static/style.js
new file mode 100644
index 000000000..279403170
--- /dev/null
+++ b/docs/_theme/_static/style.js
@@ -0,0 +1,23 @@
+
+$(document).ready(function () {
+ var blocks = $("div.configuration-block");
+
+ blocks.find("[class^=highlight-]").hide();
+ blocks.addClass("jsactive clearfix");
+ blocks.each(function () {
+ var i = $("[class^=highlight-]:first", $(this));
+ i.show();
+ i.parents("ul:eq(0)").height(i.height() + 40);
+ });
+ blocks.find("li").each(function () {
+ var i = $(":first", $(this)).html();
+ $(":first ", $(this)).html(""), $(":first ", $(this)).append('' + i + ""), $(":first", $(this)).bind("click", function () {
+ $("[class^=highlight-]", $(this).parents("ul")).hide(), $("li", $(this).parents("ul")).removeClass("selected"), $(this).parent().addClass("selected");
+ var i = $("[class^=highlight-]", $(this).parent("li"));
+ return i.show(), i.parents("ul:eq(0)").height(i.height() + 40), !1
+ })
+ });
+ blocks.each(function () {
+ $("li:first", $(this)).addClass("selected")
+ })
+});
diff --git a/docs/_theme/_templates/layout.html b/docs/_theme/_templates/layout.html
new file mode 100644
index 000000000..80c82837b
--- /dev/null
+++ b/docs/_theme/_templates/layout.html
@@ -0,0 +1,4 @@
+{% extends "!layout.html" %}
+
+{% set css_files = css_files + ['_static/style.css'] %}
+{% set script_files = script_files + ["_static/style.js"] %}
diff --git a/docs/attribute_translation.rst b/docs/attribute_translation.rst
new file mode 100644
index 000000000..de96f8d15
--- /dev/null
+++ b/docs/attribute_translation.rst
@@ -0,0 +1,105 @@
+Route Attribute Translation
+===========================
+
+If the static parts of your routes are translated you get to the point really
+fast when dynamic parts such as product slugs, category names or other dynamic
+routing parameters should be translated. The bundle provides 2 implementations.
+
+After configuring the backend you want to use (see below for each one), you
+can define a to be translated attribute in your route defaults:
+
+.. code-block:: yaml
+
+ product_view:
+ locales: { en: "/product/{slug}", de: "/produkt/{slug}" }
+ defaults: { _controller: "ShopBundle:Product:view", _translate: "slug" }
+
+ product_view2:
+ locales: { en: "/product/{category}/{slug}", de: "/produkt/{category}/{slug}" }
+ defaults:
+ _controller: "ShopBundle:Product:view"
+ _translate: ["slug", "category"]
+
+The same goes with generating routes, now backwards:
+
+.. code-block:: html+jinja
+
+ {{ path("product_view", {"slug": product.slug, "translate": "slug"}) }}
+ {{ path("product_view2", {"slug": product.slug, "translate": ["slug", "category]}) }}
+
+The reverse translation is only necessary if you have the "original" values
+in your templates. If you have access to the localized value of the current
+locale then you can just pass this and do not hint to translate it with the
+"translate" key.
+
+Doctrine DBAL Backend
+---------------------
+
+Configure the use of the DBAL backend
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ attribute_translator:
+ type: doctrine_dbal
+ connection: default # Doctrine DBAL connection name. Using null (default value) will use the default connection
+ cache: apc
+
+The Doctrine Backend has the following table structure:
+
+.. code-block:: sql
+
+ CREATE TABLE routing_translations (
+ id INT NOT NULL,
+ route VARCHAR(255) NOT NULL,
+ locale VARCHAR(255) NOT NULL,
+ attribute VARCHAR(255) NOT NULL,
+ localized_value VARCHAR(255) NOT NULL,
+ original_value VARCHAR(255) NOT NULL,
+ UNIQUE INDEX UNIQ_291BA3522C420794180C698FA7AEFFB (route, locale, attribute),
+ INDEX IDX_291BA352D951F3E4 (localized_value),
+ PRIMARY KEY(id)
+ ) ENGINE = InnoDB;
+
+Lookups are made through the combination of route name, locale and attribute
+of the route to be translated.
+
+Every lookup is cached in a Doctrine\Common\Cache\Cache instance that you
+should configure to be APC, Memcache or Xcache for performance reasons.
+
+If you are using Doctrine it automatically registers a listener for SchemaTool
+to create the routing_translations table for your database backend, you only
+have to call:
+
+.. code-block:: bash
+
+ ./app/console doctrine:schema:update --dump-sql
+ ./app/console doctrine:schema:update --force
+
+Translator backend
+------------------
+
+This implementation uses the Symfony2 translator to translate the attributes.
+The translation domain will be created using the pattern `_`
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ attribute_translator:
+ type: translator
+
+Custom backend
+~~~~~~~~~~~~~~
+
+If you want to use a different implementation, simply create a service implementing
+`BeSimple\I18nRoutingBundle\Routing\Translator\AttributeTranslatorInterface`.
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ attribute_translator:
+ type: service
+ id: my_attribute_translator
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 000000000..9fb894c91
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+#
+# BeSimpleI18nRoutingBundle documentation build configuration file, created by
+# sphinx-quickstart on Sat Jul 28 21:58:57 2012.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.append(os.path.abspath('_theme/_exts'))
+
+# adding PhpLexer
+from sphinx.highlighting import lexers
+from pygments.lexers.compiled import CLexer
+from pygments.lexers.special import TextLexer
+from pygments.lexers.text import RstLexer
+from pygments.lexers.web import PhpLexer
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.todo',
+ 'sensio.sphinx.refinclude',
+ 'sensio.sphinx.configurationblock',
+ 'sensio.sphinx.phpcode',
+ 'sensio.sphinx.codeblock'
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_theme/_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'BeSimple - I18n Routing Bundle Documentation'
+copyright = ''
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+# version = '2.2'
+# The full version, including alpha/beta/rc tags.
+# release = '2.2.13'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_theme']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# -- Settings for symfony doc extension ---------------------------------------------------
+
+# enable highlighting for PHP code not between ```` by default
+lexers['markdown'] = TextLexer()
+lexers['php'] = PhpLexer(startinline=True)
+lexers['php-annotations'] = PhpLexer(startinline=True)
+lexers['php-standalone'] = PhpLexer(startinline=True)
+lexers['rst'] = RstLexer()
+
+config_block = {
+ 'markdown': 'Markdown',
+ 'rst': 'reStructuredText'
+}
+
+# use PHP as the primary domain
+primary_domain = 'php'
+
+# set url for API links
+# api_url = 'http://api.symfony.com/master/%s'
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_theme/_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'BeSimpleI18nRoutingBundleDoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'BeSimpleI18nRoutingBundle.tex', u'BeSimple - I18n Routing Bundle Documentation',
+ u'BeSimple I18n Routing Bundle community', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'BeSimpleI18nRoutingBundle', u'BeSimple - I18n Routing Bundle Documentation',
+ [u'BeSimple - I18n Routing Bundle community'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'BeSimpleI18nRoutingBundle', u'BeSimple - I18n Routing Bundle Documentation',
+ u'BeSimple - I18n Routing Bundle community', 'Full routing internationalized on your Symfony2 project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# Use PHP syntax highlighting in code examples by default
+highlight_language='php'
+
diff --git a/docs/customize_router.rst b/docs/customize_router.rst
new file mode 100644
index 000000000..bff69b1e1
--- /dev/null
+++ b/docs/customize_router.rst
@@ -0,0 +1,69 @@
+Customizing the Router
+======================
+
+Route Naming
+------------
+
+By default all routes that are imported are named '.' but sometimes you may want to change this behaviour.
+To do this you can specify a route name inflector service in your configuration as followed.
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ # Service must implement the `BeSimple\I18nRoutingBundle\Routing\RouteGenerator\NameInflector\RouteNameInflectorInterface`
+ route_name_inflector: "my_route_name_inflector_service"
+
+There are currently 2 inflectors available by default ``be_simple_i18n_routing.route_name_inflector.postfix`` and ``be_simple_i18n_routing.route_name_inflector.default_postfix``.
+
+Default Postfix Inflector
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The default postfix inflector changed the behaviour to only add a locale postfix when the locale is not the default locale.
+For this to work correctly you must configure a ``default_locale``.
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ route_name_inflector: 'be_simple_i18n_routing.route_name_inflector.default_postfix'
+ locales:
+ default_locale: '%kernel.default_locale%'
+
+
+Route Filtering
+---------------
+
+During the development of your system you may want to filter out a (not fully) implemented locale.
+This can be done why configuring the supported locales and enabling filtering.
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ locales:
+ supported: ['en', 'nl']
+ filter: true
+
+The above configuration will only generate routes for the ``en`` and ``nl`` locale and will ignore any other locales.
+
+
+Strict Route Locales
+--------------------
+
+During development it can be nice to ensure that all routes are localized.
+This can be done why configuring the supported locales and enabling strict localization.
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ locales:
+ supported: ['en', 'nl']
+ strict: true
+
+The ``strict`` options has 3 settings:
+
+- ``true`` for throwing a exception when a i18n route is found where the locale is unknown or where a locale is missing.
+- ``null`` for throwing a exception where a locale is missing.
+- ``false`` for disabling strict routes (default)
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 000000000..82a1a947d
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,358 @@
+Using BeSimpleI18nRoutingBundle
+===============================
+
+Welcome to BeSimpleI18nRoutingBundle - generate I18N Routes simply and quickly
+
+.. toctree::
+ :hidden:
+
+ self
+
+Installation
+------------
+
+Step 1: Download the Bundle
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Open a command console, enter your project directory and execute the
+following command to download the latest stable version of this bundle:
+
+.. code-block:: bash
+
+ $ composer require besimple/i18n-routing-bundle "^3.0"
+
+This command requires you to have Composer installed globally, as explained
+in the `installation chapter`_ of the Composer documentation.
+
+.. _`installation chapter`: https://getcomposer.org/doc/00-intro.md
+
+Step 2: Enable the Bundle
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Then, enable the bundle by adding the following line in the ``app/AppKernel.php``
+file of your project:
+
+.. code-block:: php
+
+ // app/AppKernel.php
+
+ // ...
+ class AppKernel extends Kernel
+ {
+ public function registerBundles()
+ {
+ $bundles = array(
+ // ...
+
+ new BeSimple\I18nRoutingBundle\BeSimpleI18nRoutingBundle(),
+ );
+
+ // ...
+ }
+
+ // ...
+ }
+
+
+Step 3: (optional) Configure the bundle
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The bundle comes with a sensible default configuration, which is listed below.
+If you skip this step, these defaults will be used.
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ be_simple_i18n_routing:
+ # if true, enables the annotation route loader
+ annotations: true
+
+ locales:
+ # the default locale, used when generating routes
+ default_locale: null
+ # the supported locales used by "filter" and "strict"
+ supported: []
+ # if true, filter out any locales not in "supported"
+ filter: false
+ # if true, filter out any locales not in "supported"
+ strict: false
+
+ attribute_translator:
+ # if true, enables the attribute translator
+ enabled: false
+
+ # the type of attribute translator to use
+ type: translator
+ # When defining type as "service" then
+ # add the id parameter with the service id to use (e.g. id: "my_attribute_translator_service_id")
+ # and ensure the service implements "\BeSimple\I18nRoutingBundle\Routing\Translator\AttributeTranslatorInterface"
+ #
+ # When defining type as "doctrine_dbal" then
+ # optionally add the connection parameter to set the dbal connection name (e.g. connection: "connection_name")
+ # optionally add a caching configuration using the cache parameter:
+ # type: array | apc | xcache | memcache
+ # when the cache type is "memcache" then (optionally) add the connection information:
+ # type: memcache
+ # host: 127.0.0.1
+ # port: 11211
+ # instance_class: Memcache
+ # class: \Doctrine\Common\Cache\MemcacheCache
+
+ # the route name inflector service
+ route_name_inflector: 'be_simple_i18n_routing.route_name_inflector.postfix'
+
+
+Create your localized routes!
+-----------------------------
+
+Importing Routes
+~~~~~~~~~~~~~~~~
+
+To define internationalized routes, you need to import the routing file using the ``be_simple_i18n`` type:
+
+.. code-block:: yaml
+
+ # app/config/routing.yml
+
+ my_yaml_i18n_routes:
+ type: be_simple_i18n
+ resource: "@AppBundle/Resources/config/routing/i18n.yml"
+
+ my_xml_i18n_routes:
+ type: be_simple_i18n
+ resource: "@AppBundle/Resources/config/routing/i18n.xml"
+
+ # For annotation support ensure that annotations is true in the configuration
+ my_annotation_i18n_routes:
+ resource: '@AppBundle/Controller/'
+ type: annotation
+
+Defining Routes
+~~~~~~~~~~~~~~~
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # @AppBundle/Resources/config/routing/i18n.yml
+ homepage:
+ path: /blog/{slug}
+ defaults: { _controller: AppBundle:Frontend:index }
+
+ .. code-block:: xml
+
+
+
+
+
+
+ /welcome
+ /bienvenue
+ /willkommen
+ AppBundle:Frontend:index
+
+
+
+ .. code-block:: php
+
+ // app/config/routing.php
+ use Symfony\Component\Routing\RouteCollection;
+ use BeSimple\I18nRoutingBundle\Routing\RouteGenerator\I18nRouteGenerator;
+
+ $generator = new I18nRouteGenerator();
+
+ $collection = new RouteCollection();
+ $collection->addCollection(
+ $generator->generateRoutes(
+ 'homepage',
+ array(
+ 'en' => '/welcome',
+ 'fr' => '/bienvenue',
+ 'de' => '/willkommen'
+ ),
+ new Route('', array(
+ '_controller' => 'AppBundle:Frontend:index'
+ ))
+ )
+ );
+
+ return $collection;
+
+ .. code-block:: php-annotations
+
+ // @AppBundle/Controller/BlogController.php
+ namespace AppBundle\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use BeSimple\I18nRoutingBundle\Routing\Annotation\I18nRoute;
+
+ class FrontendController extends Controller
+ {
+ /**
+ * @I18nRoute({ "en": "/welcome/{name}", "fr": "/bienvenue/{name}", "de": "/willkommen/{name}" }, name="homepage")
+ */
+ public function indexAction($name)
+ {
+ // ...
+ }
+ }
+
+Using Normal Routes
+~~~~~~~~~~~~~~~~~~~
+
+Sometimes you have routes that don't need to be translated.
+To allow this simply add the routes as followed.
+
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # @AppBundle/Resources/config/routing/i18n.yml
+ homepage:
+ path: "/{name}"
+ defaults: { _controller: AppBundle:Hello:index }
+
+ welcome:
+ locales: { en: "/welcome/{name}", fr: "/bienvenue/{name}", de: "/willkommen/{name}" }
+ defaults: { _controller: AppBundle:Frontend:welcome }
+
+ .. code-block:: xml
+
+
+
+
+
+
+ AppBundle:Hello:index
+
+
+ /welcome/{name}
+ /bienvenue/{name}
+ /willkommen/{name}
+ AppBundle:Frontend:index
+
+
+
+ .. code-block:: php
+
+ // app/config/routing.php
+ use BeSimple\I18nRoutingBundle\Routing\RouteGenerator\I18nRouteGenerator;
+ use Symfony\Component\Routing\RouteCollection;
+ use Symfony\Component\Routing\Route;
+
+ $generator = new I18nRouteGenerator();
+
+ $collection = new RouteCollection();
+ $collection->add('hello', new Route('/hello/{name}', array(
+ '_controller' => 'AppBundle:Hello:index',
+ )));
+ $collection->addCollection(
+ $generator->generateRoutes(
+ 'homepage',
+ array('en' => '/welcome/{name}', 'fr' => '/bienvenue/{name}', 'de' => '/willkommen/{name}'),
+ new Route('', array(
+ '_controller' => 'AppBundle:Frontend:index',
+ ))
+ )
+ );
+
+ return $collection;
+
+ .. code-block:: php-annotations
+
+ // @AppBundle/Controller/BlogController.php
+ namespace AppBundle\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use BeSimple\I18nRoutingBundle\Routing\Annotation\I18nRoute;
+
+ class FrontendController extends Controller
+ {
+ /**
+ * @I18nRoute("/{name}", name="hello")
+ */
+ public function helloAction($slug)
+ {
+ // ...
+ }
+
+ /**
+ * @I18nRoute({ "en": "/welcome/{name}", "fr": "/bienvenue/{name}", "de": "/willkommen/{name}" }, name="homepage")
+ */
+ public function indexAction($name)
+ {
+ // ...
+ }
+ }
+
+Prefixing Imported Routes
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can also choose to provide a "prefix" for the imported routes.
+Combining this with normal routes will automatically localize them.
+
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/routing.yml
+ app:
+ resource: '@AppBundle/Controller/'
+ type: be_simple_i18n
+ prefix:
+ en: /website
+ fr: /site
+ de: /webseite
+
+ .. code-block:: xml
+
+
+
+
+
+
+ /english
+ /german
+ /french
+
+
+
+ .. code-block:: php
+
+ // app/config/routing.php
+ use BeSimple\I18nRoutingBundle\Routing\RouteGenerator\I18nRouteGenerator;
+ use Symfony\Component\Routing\RouteCollection;
+
+ $generator = new I18nRouteGenerator();
+
+ $app = $loader->import('@AppBundle/Controller/', 'annotation');
+ $app = $generator->generateCollection(array(
+ 'en' => '/english',
+ 'de' => '/german',
+ 'fr' => '/french',
+ ), $app);
+
+ $collection = new RouteCollection();
+ $collection->addCollection($app);
+
+ return $collection;
+
+
+More Stuff
+----------
+
+.. toctree::
+ :maxdepth: 1
+
+ route_generation
+ attribute_translation
+ customize_router
diff --git a/docs/route_generation.rst b/docs/route_generation.rst
new file mode 100644
index 000000000..be9fdac4e
--- /dev/null
+++ b/docs/route_generation.rst
@@ -0,0 +1,46 @@
+Route Generation
+================
+
+Using a specify locale
+----------------------
+
+.. configuration-block::
+
+ .. code-block:: html+jinja
+
+ {{ path('homepage.en') }}
+ {{ path('homepage', { 'locale': 'en' }) }}
+
+ {{ path('homepage.fr') }}
+ {{ path('homepage', { 'locale': 'fr' }) }}
+
+ {{ path('homepage.de') }}
+ {{ path('homepage', { 'locale': 'de' }) }}
+
+ .. code-block:: html+php
+
+ generate('homepage.en') ?>
+ generate('homepage', array('locale' => 'en')) ?>
+ generate('homepage.fr') ?>
+ generate('homepage', array('locale' => 'fr')) ?>
+ generate('homepage.de') ?>
+ generate('homepage', array('locale' => 'de')) ?>
+
+
+.. note::
+
+ When using the locale to generate the route make sure you use the ``locale`` parameter and not ``_locale``.
+
+
+Using the current locale
+------------------------
+
+.. configuration-block::
+
+ .. code-block:: html+jinja
+
+ {{ path('homepage') }}
+
+ .. code-block:: html+php
+
+ generate('homepage') ?>